स्ट्रीम <T> Iterable <T> को लागू क्यों नहीं करता है?


261

जावा 8 में हमारे पास क्लास स्ट्रीम <टी> है , जो उत्सुकता से एक विधि है

Iterator<T> iterator()

तो आप उम्मीद करेंगे कि यह इंटरफ़ेस Iterable <T> को लागू करे , जिसके लिए इस पद्धति की आवश्यकता है, लेकिन ऐसा नहीं है।

जब मैं एक फॉरेस्ट लूप का उपयोग करके स्ट्रीम पर पुनरावृति करना चाहता हूं, तो मुझे कुछ ऐसा करना होगा

public static Iterable<T> getIterable(Stream<T> s) {
    return new Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return s.iterator();
        }
    };
}

for (T element : getIterable(s)) { ... }

क्या मुझसे कोई चूक हो रही है?


7
यह उल्लेख करने के लिए कि पुनरावृत्ति के अन्य 2 तरीके (forEach और spliterator) भी स्ट्रीम में हैं
njzk2

1
इसके Streamलिए विरासत एपीआई को पास करने की जरूरत है जो उम्मीद करता हैIterable
झोंग्यु

11
एक अच्छा आईडीई (जैसे इंटेलीजे) में अपने कोड को आसान बनाने के लिए कहा जाएगा getIterable()करने के लिएreturn s::iterator;
Zhongyu

23
आपको किसी विधि की आवश्यकता नहीं है। जहाँ आपके पास एक स्ट्रीम है, और एक Iterable चाहते हैं, बस स्ट्रीम पास करें: itter (या, यदि आप चाहें, तो () -> stream.iterator ()), और आप कर रहे हैं।
ब्रायन गोएत्ज

6
दुर्भाग्य से मैं लिख नहीं सकता for (T element : stream::iterator), इसलिए मैं अभी भी पसंद करूंगा अगर स्ट्रीम भी लागू होगी Iterableया एक विधि toIterable()
थॉरस्टेन

जवाबों:


197

लोग मेलिंग सूची People पर पहले से ही पूछ चुके हैं । Iterable का मुख्य कारण पुन: चलने योग्य शब्दार्थ भी है, जबकि स्ट्रीम नहीं है।

मुझे लगता है कि मुख्य कारण यह है कि Iterableपुन: प्रयोज्यता का अर्थ है, जबकि Streamऐसा कुछ है जो केवल एक बार उपयोग किया जा सकता है - जैसे कि एक Iterator

यदि Streamविस्तारित है Iterableतो मौजूदा कोड आश्चर्यचकित हो सकता है जब यह प्राप्त करता है Iterableकि Exceptionदूसरी बार वे फेंकता है for (element : iterable)


22
उत्सुकता से जावा 7 में इस व्यवहार के साथ पहले से ही कुछ पुनरावृत्तियाँ थीं, जैसे DirectoryStream: जबकि DirectoryStream Iterable का विस्तार करता है, यह एक सामान्य-उद्देश्य Iterable नहीं है क्योंकि यह केवल एक Iterator का समर्थन करता है; एक या बाद में पुनरावृत्ति प्राप्त करने के लिए पुनरावृत्ति विधि को लागू करना IllegalStateException को फेंकता है। ( openjdk.java.net/projects/nio/javadoc/java/nio/file/… )
roim

31
दुर्भाग्य से वहाँ कुछ भी नहीं है के Iterableबारे में प्रलेखन है कि iteratorक्या हमेशा या कई बार कॉल करने योग्य नहीं हो सकता है। यह कुछ ऐसा है जिसे उन्हें वहां रखना चाहिए। यह एक औपचारिक विनिर्देश की तुलना में एक मानक अभ्यास से अधिक प्रतीत होता है।
Lii

7
यदि वे किसी बहाने का उपयोग करने जा रहे हैं, तो आपको लगता है कि वे कम से कम उन सभी तरीकों के लिए एक asIterable () विधि जोड़ सकते हैं - या ओवरलोड कर सकते हैं जो केवल Iterable लेते हैं।
तर्जुक

25
हो सकता है कि सबसे अच्छा समाधान यह हो कि जावा के फॉर्चेस ने Iterable <T> और साथ ही संभावित रूप से स्ट्रीम <T> को स्वीकार किया हो?
निगल Elysium

3
@Lii मानक प्रथाओं के अनुसार, यह हालांकि एक बहुत मजबूत है।
बाइसिक्लोप

160

A को कन्वर्ट करने के Streamलिए Iterable, आप कर सकते हैं

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

Streamउम्मीद है कि एक विधि पारित करने के लिए Iterable,

void foo(Iterable<X> iterable)

केवल

foo(stream::iterator) 

हालाँकि यह शायद मजाकिया लगता है; थोड़ा और अधिक स्पष्ट होना बेहतर हो सकता है

foo( (Iterable<X>)stream::iterator );

66
आप इसे लूप में भी उपयोग कर सकते हैं for(X x : (Iterable<X>)stream::iterator), हालांकि यह बदसूरत दिखता है। वास्तव में, पूरी स्थिति सिर्फ बेतुका है।
डबिन्स्की

24
@ एचआरजेIntStream.range(0,N).forEach(System.out::println)
माइकफा

11
मैं इस संदर्भ में डबल कोलोन सिंटैक्स को नहीं समझता। क्या अंतर है stream::iteratorऔर stream.iterator(), जो पूर्व को स्वीकार्य बनाता है Iterableलेकिन बाद के लिए नहीं?
डैनियल सी। सोबरल

20
अपने आप को जवाब देना: Iterableएक कार्यात्मक इंटरफ़ेस है, इसलिए एक फ़ंक्शन को पास करना जो इसे लागू करता है वह पर्याप्त है।
डैनियल सी। सोबरल

5
यह ध्यान देने योग्य है कि यह टूट जाएगा यदि प्राप्त कोड Iterable (उदाहरण के लिए प्रत्येक दो अलग-अलग छोरों) का उपयोग करने का प्रयास करता है, तो kennytm के उत्तर के अनुसार । यह तकनीकी रूप से कल्पना को तोड़ देता है। आप केवल एक बार एक धारा का उपयोग कर सकते हैं; आप बेसस्ट्रीम :: इटरेटर को दो बार कॉल नहीं कर सकते। पहली कॉल धारा को समाप्त करती है। JavaDoc के अनुसार: "यह एक टर्मिनल ऑपरेशन है।" यदि यह सुविधाजनक है, तो सुविधाजनक होने पर, आपको परिणामी Iterable को पास नहीं करना चाहिए जो आपके अंतिम नियंत्रण में नहीं है।
Zenexer


8

आप एक forलूप में स्ट्रीम का उपयोग इस प्रकार कर सकते हैं:

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

( इस स्निपेट को यहां चलाएं )

(यह जावा 8 फंक्शनल इंटरफेस कास्ट का उपयोग करता है।)

(यह ऊपर की कुछ टिप्पणियों में समाहित है (जैसे अलेक्सांद्र डबिन्सकी ), लेकिन मैं इसे और अधिक दृश्यमान बनाने के लिए उत्तर में निकालना चाहता था।)


लगभग सभी को इसे देखने से पहले दो बार या तीन बार देखने की जरूरत है कि यह कैसे संकलित करता है। यह बेतुका है। (यह उसी टिप्पणी स्ट्रीम में टिप्पणियों के जवाब में कवर किया गया है, लेकिन मैं इसे और अधिक दृश्यमान बनाने के लिए यहां टिप्पणी जोड़ना चाहता था।)
ShioT

7

kennytm वर्णित कारण है कि यह एक के इलाज के लिए असुरक्षित है Streamएक के रूप में Iterable, और झोंग यू एक समाधान की पेशकश की है कि एक के उपयोग की अनुमति Streamके रूप में Iterable, एक असुरक्षित तरीके से यद्यपि। एक पुन: प्रयोज्य: यह दोनों दुनिया का सबसे अच्छा पाने के लिए संभव है Iterableएक से Streamहै कि सभी के द्वारा बनाई गई गारंटी को पूरा करती है Iterableविनिर्देश।

नोट: SomeTypeयहां एक प्रकार का पैरामीटर नहीं है - आपको इसे एक उचित प्रकार (जैसे, String) या प्रतिबिंब के लिए सहारा के साथ बदलने की आवश्यकता है

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

एक प्रमुख नुकसान है:

आलसी पुनरावृत्ति का लाभ खो जाएगा। यदि आपने वर्तमान थ्रेड में सभी मूल्यों पर तुरंत पुनरावृति करने की योजना बनाई है, तो कोई भी ओवरहेड नगण्य होगा। हालाँकि, यदि आपने केवल आंशिक या अलग थ्रेड में पुनरावृति करने की योजना बनाई है, तो इस तात्कालिक और पूर्ण पुनरावृत्ति के अनपेक्षित परिणाम हो सकते हैं।

बेशक, इसका बड़ा फायदा यह है कि आप इसका फिर से उपयोग कर सकते हैं Iterable, जबकि (Iterable<SomeType>) stream::iteratorकेवल एक ही उपयोग की अनुमति होगी। यदि प्राप्त कोड कई बार संग्रह पर पुनरावृत्त होगा, तो यह न केवल आवश्यक है, बल्कि प्रदर्शन के लिए फायदेमंद है।


1
क्या आपने उत्तर देने से पहले अपना कोड संकलित करने की कोशिश की है? यह काम नहीं करता है।
टैगिर वलेव

1
@TagirValeev हाँ, मैंने किया। आपको उपयुक्त प्रकार के साथ टी को बदलने की आवश्यकता है। मैंने काम कोड से उदाहरण की नकल की।
ज़ेनेक्सर

2
@TagirValeev मैंने इसे IntelliJ में फिर से परीक्षण किया। ऐसा प्रतीत होता है कि इंटेलीज कभी-कभी इस वाक्य-विन्यास से भ्रमित हो जाता है; मुझे वास्तव में इसका कोई पैटर्न नहीं मिला है। हालाँकि, कोड ठीक संकलित करता है, और IntelliJ संकलन के बाद त्रुटि सूचना निकालता है। मुझे लगता है कि यह सिर्फ एक बग है।
जेनेक्सर

1
Stream.toArray()एक सरणी देता है, नहीं Iterable, तो यह कोड अभी भी संकलित नहीं करता है। लेकिन यह ग्रहण में एक बग हो सकता है, क्योंकि IntelliJ इसे संकलित करने के लिए लगता है
benez

1
@Zenexer आप किसी सरणी को Iterable में कैसे निर्दिष्ट कर सकते हैं?
radiantRazor

3

Streamलागू नहीं करता है Iterable। सामान्य समझ ऐसी Iterableचीज़ है, जिसे बार-बार दोहराया जा सकता है। Streamहो सकता है कि पुन: प्रयोज्य न हो।

एकमात्र ऐसा हल जो मैं सोच सकता हूं, जहां एक स्ट्रीम पर आधारित पुनरावृत्ति भी पुन: प्रयोज्य है, स्ट्रीम को फिर से बनाना है। मैं Supplierधारा का एक नया उदाहरण बनाने के लिए नीचे का उपयोग कर रहा हूं , हर बार एक नया पुनरावृत्त बनाया जाता है।

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }

2

अगर आपको थर्ड पार्टी लाइब्रेरियों का उपयोग करने में कोई आपत्ति नहीं है, तो साइक्लॉप्स-रिएक्शन एक स्ट्रीम को परिभाषित करता है, जो स्ट्रीम और इरेटेबल दोनों को लागू करता है और रिप्लेसेबल भी है (समस्या का वर्णन kennytm को हल करना )।

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

या: -

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[प्रकटीकरण मैं चक्रवात-प्रतिक्रिया का प्रमुख विकासकर्ता हूं]


0

सही नहीं है, लेकिन काम करेंगे:

iterable = stream.collect(Collectors.toList());

बिल्कुल सही नहीं है क्योंकि यह धारा से सभी वस्तुओं को लाएगा और उन्हें उस में डाल देगा List, जो वास्तव में नहीं है Iterableऔर Streamइसके बारे में क्या है । उन्हें आलसी माना जाता है ।


-1

आप Stream<Path>इस तरह का उपयोग कर एक फ़ोल्डर में सभी फ़ाइलों पर पुनरावृति कर सकते हैं :

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.