Java का Iterator Iterable क्यों नहीं है?


178

Iteratorइंटरफ़ेस का विस्तार क्यों नहीं होता है Iterable?

iterator()विधि बस लौट सकते हैं this

यह उद्देश्य पर है या सिर्फ जावा के डिजाइनरों की निगरानी है?

इस तरह से पुनरावृत्तियों के साथ प्रत्येक लूप का उपयोग करने में सक्षम होना सुविधाजनक होगा:

for(Object o : someContainer.listSomeObjects()) {
    ....
}

जहां listSomeObjects()एक पुनरावृति देता है।


1
ठीक है - मैं आपकी बात देख रहा हूँ। यह अभी भी सुविधाजनक होगा, भले ही यह एक शब्दार्थ को थोड़ा तोड़ दे:] सभी उत्तर के लिए धन्यवाद:]
ikukasz Bownik

मुझे लगता है कि यह सवाल बहुत पहले पूछा गया था, लेकिन क्या आपका मतलब किसी संग्रहकर्ता से है, या कुछ संग्रह से संबंधित सिर्फ एक Iterator से है?
einpoklum

जवाबों:


67

क्योंकि एक पुनरावृत्त आम तौर पर एक संग्रह में एकल उदाहरण को इंगित करता है। Iterable का तात्पर्य है कि व्यक्ति अपने तत्वों पर पार करने के लिए किसी वस्तु से एक पुनरावृत्ति प्राप्त कर सकता है - और एक उदाहरण पर इसे पुनरावृत्त करने की आवश्यकता नहीं है, जो कि एक पुनरावृत्ति का प्रतिनिधित्व करता है।


25
+1: एक संग्रह चलने योग्य है। एक पुनरावृति iterable नहीं है क्योंकि यह एक संग्रह नहीं है।
S.Lott

50
हालांकि मैं जवाब से सहमत हूं, मुझे नहीं पता कि क्या मैं मानसिकता से सहमत हूं। Iterable इंटरफ़ेस एक एकल विधि प्रस्तुत करता है: Iterator <?> Iterator (); जो भी मामले में, मुझे प्रत्येक के लिए एक पुनरावृत्ति निर्दिष्ट करने में सक्षम होना चाहिए। मैं इसे नहीं खरीदता।
क्रिस के

25
@ S.Lott अच्छा परिपत्र तर्क वहाँ।
yihtserns

16
@ S.Lott अंतिम प्रयास: संग्रह .L योग्य। Iterator ter संग्रह ter Iterator
Iterable

27
@ S.Lott: संग्रह इस चर्चा के लिए पूरी तरह अप्रासंगिक है। संग्रह Iterable के कई संभावित कार्यान्वयनों में से एक है। तथ्य यह है कि कुछ संग्रह नहीं है, इस पर कोई असर नहीं है कि यह एक Iterable है।
कॉलिनड

218

एक पुनरावृत्त राज्य है। विचार यह है कि यदि आप Iterable.iterator()दो बार कॉल करते हैं, तो आपको स्वतंत्र पुनरावृत्तियों मिलेंगे - अधिकांश पुनरावृत्तियों के लिए, वैसे भी। यह स्पष्ट रूप से आपके परिदृश्य में मामला नहीं होगा।

उदाहरण के लिए, मैं आमतौर पर लिख सकता हूं:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

उस संग्रह को दो बार प्रिंट करना चाहिए - लेकिन आपकी योजना के साथ दूसरा लूप हमेशा तुरंत समाप्त हो जाएगा।


17
@ क्रिस: यदि एक कार्यान्वयन एक ही पुनरावृत्तिकर्ता को दो बार लौटाता है, तो पृथ्वी पर यह इटरेटर के अनुबंध को कैसे पूरा कर सकता है? यदि आप कॉल iteratorकरते हैं और परिणाम का उपयोग करते हैं, तो इसे संग्रह पर पुनरावृति करना होगा - जो कि ऐसा नहीं करेगा यदि वही वस्तु पहले ही संग्रह पर पुनरावृत्त हो गई हो। क्या आप कोई सही क्रियान्वयन दे सकते हैं (खाली संग्रह के अलावा) जहां एक ही पुनरावृत्तिकर्ता दो बार लौटा हो?
जॉन स्कीट

7
यह एक शानदार प्रतिक्रिया है जॉन, आपको वास्तव में यहाँ समस्या का क्रूस मिला है। शर्म की बात है कि यह स्वीकृत उत्तर नहीं है! Iterable के लिए अनुबंध सख्ती से परिभाषित किया गया है, लेकिन उपरोक्त कारणों का एक बड़ा विवरण है कि Iterator को Iterable (foreach) को लागू करने की अनुमति देने से इंटरफ़ेस की भावना टूट जाएगी।
joelittlejohn

3
@JonSkeet जबकि एक Iterator <T> स्टेटफुल है, Iterable <T> का अनुबंध कहता है कि स्वतंत्र पुनरावृत्तियों को प्राप्त करने के लिए दो बार उपयोग करने में सक्षम होने के बारे में कुछ भी नहीं है, यहां तक ​​कि 99% मामलों में यह परिदृश्य है। सभी Iterable <T> कहते हैं कि यह किसी ऑब्जेक्ट को फॉर्च्यूनर के लक्ष्य के लिए अनुमति देता है। आप में से जो लोग Iterator से नाखुश हैं <T> Iterable <T> नहीं हैं, आप ऐसे Iterator <T> को बनाने के लिए स्वतंत्र हैं। यह अनुबंध को एक सा नहीं तोड़ता। हालाँकि - Iterator अपने आप में Iterable नहीं होना चाहिए क्योंकि यह परिपत्र रूप से निर्भर करेगा और यह icky डिजाइन के लिए जमीन देता है।
सेंट्रिल

2
@ केंद्र: सही। यह इंगित करने के लिए संपादित करें कि आमतौर परiterable दो बार कॉल करने से आपको स्वतंत्र पुनरावृत्तियाँ मिलेंगी।
जॉन स्कीट

2
इससे इसका दिल दहल जाता है। लगभग एक रीसेट Iterator को लागू करना संभव होगा। खुद को रीसेट करके। प्रत्येक छोरों के लिए घोंसले से तत्वों का।
थियोडोर मर्डॉक

60

मेरे $ 0.02 के लिए, मैं पूरी तरह से सहमत हूं कि Iterator को Iterable को लागू नहीं करना चाहिए, लेकिन मुझे लगता है कि लूप के लिए बढ़ा हुआ या तो स्वीकार करना चाहिए। मुझे लगता है कि पूरे "पुनरावृत्तियों को चलने योग्य बनाते हैं" तर्क भाषा में एक दोष के आसपास एक काम के रूप में आता है।

लूप के लिए एन्हांस किए जाने का पूरा कारण यह था कि यह "संग्रहकर्ताओं और सरणियों पर पुनरावृत्ति करते समय पुनरावृत्तियों और सूचकांक चर की drudgery और त्रुटि-स्पष्टता को समाप्त करता है" [ 1 ]।

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

फिर यह वही तर्क चलने वालों के लिए क्यों नहीं है?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

दोनों मामलों में, hasNext () और next () के कॉल हटा दिए गए हैं, और इनर लूप में चलने वाले का कोई संदर्भ नहीं है। हां, मैं समझता हूं कि Iterables का उपयोग कई पुनरावृत्तियों को बनाने के लिए किया जा सकता है, लेकिन यह सब लूप के बाहर होता है: लूप के अंदर कभी भी एक आगे की प्रगति केवल एक आइटम पर होती है जो पुनरावृत्तिकर्ता द्वारा दिए गए आइटम पर होती है।

इसके अलावा, इसकी अनुमति देने से Enumerations के लिए लूप के लिए उपयोग करना भी आसान हो जाएगा, जो कि कहीं और बताया गया है, Iterators के अनुरूप हैं Iterables नहीं।

इसलिए Iterator को Iterable कार्यान्वित न करें, लेकिन स्वीकार करने के लिए लूप के लिए अद्यतन करें।

चीयर्स,


6
मैं सहमत हूँ। सैद्धांतिक रूप से इट्रेटर प्राप्त करते समय भ्रम हो सकता है, इसके भाग का उपयोग करना, और फिर इसे फ़ॉर्च में डाल देना (फ़ॉरच्यू के "प्रत्येक" अनुबंध को तोड़ना), लेकिन मुझे नहीं लगता कि यह सुविधा न होने का एक अच्छा पर्याप्त कारण है।
बार्ट वैन ह्युकेलोम

हमने पहले से ही सरणियों को स्वीकार्य संग्रह बनाने के बजाय सरणियों को स्वीकार करने के लिए लूप को अपडेट / बढ़ाया है। क्या आप इस फैसले को तर्कसंगत बना सकते हैं?
Val

मैंने इस उत्तर को गलत ठहराया, और यह एक गलती थी। Iterator स्टेटफुल है, यदि आप for(item : iter) {...}सिंटैक्स के साथ किसी इटरेटर पर पुनरावृत्ति को बढ़ावा देते हैं , तो एक ही इटरेटर दो बार iterated होने पर एक त्रुटि को भड़काएगा। कल्पना कीजिए कि Iteratorमें पारित हो जाता iterateOverविधि के बजाय Iterableमें इस उदाहरण
इल्या सिल्वेस्ट्रोव

2
यह वास्तव में मायने नहीं रखता है कि आप for (String x : strings) {...}या while (strings.hasNext()) {...}शैली का उपयोग करते हैं: यदि आप दूसरी बार दो बार एक पुनरावृत्ति के माध्यम से लूप करने का प्रयास करते हैं, तो कोई परिणाम नहीं मिलेगा, इसलिए मैं यह नहीं देखता कि अपने आप में एक तर्क के रूप में बढ़ाया सिंटैक्स की अनुमति देता है। जॉन का जवाब अलग है, वहाँ है, क्योंकि वह कैसे लपेटकर एक दिखा रहा है Iteratorएक में Iterableसमस्याओं के कारण होता है कि मामला आप कई बार के रूप में यह पुन: उपयोग करने के रूप में आप इसे पसंद किया सक्षम होने के लिए उम्मीद करेंगे के रूप में,।
बार्नी

17

जैसा कि दूसरों ने बताया, Iteratorऔर Iterableदो अलग चीजें हैं।

इसके अलावा, Iteratorकार्यान्वयन लूप के लिए बढ़ाए गए पूर्ववर्ती हैं।

यह एक साधारण एडेप्टर विधि के साथ इस सीमा को पार करने के लिए भी तुच्छ है जो स्थैतिक विधि के आयात के साथ इस तरह दिखता है:

for (String line : in(lines)) {
  System.out.println(line);
}

नमूना कार्यान्वयन:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

जावा 8 में Iteratorएक Iterableआसान हो जाता है:

for (String s : (Iterable<String>) () -> iterator) {

आपने एक समारोह में एक कक्षा घोषित की; क्या मैं कुछ भूल रहा हूँ? मैंने सोचा कि यह अवैध था।
मई

जावा में एक ब्लॉक में एक वर्ग को परिभाषित किया जा सकता है। इसे एक स्थानीय वर्ग
कॉलिन डी बेनेट

3
धन्यवाद के लिएfor (String s : (Iterable<String>) () -> iterator)
बजे AlikElzin-kilaka

8

जैसा कि दूसरों ने कहा है, एक Iterable को कई बार कहा जा सकता है, प्रत्येक कॉल पर एक नया Iterator लौटाता है; Iterator का उपयोग केवल एक बार किया जाता है। इसलिए वे संबंधित हैं, लेकिन विभिन्न उद्देश्यों की सेवा करते हैं। निराशा की बात है, हालांकि, "कॉम्पैक्ट के लिए" विधि केवल चलने के साथ काम करती है।

नीचे मैं जो वर्णन करूंगा, वह दोनों दुनिया में सबसे अच्छा करने का एक तरीका है - डेटा के अंतर्निहित अनुक्रम एक-बंद होने पर भी एक Iterable (अच्छे सिंटैक्स के लिए) वापस करना।

चाल Iterable का एक अनाम कार्यान्वयन वापस करना है जो वास्तव में काम को चालू करता है। इसलिए उस कार्य को करने के बजाय जो एक बार के अनुक्रम को उत्पन्न करता है और फिर उस पर एक Iterator को लौटाता है, आप एक Iterable को लौटाते हैं, जो हर बार एक्सेस होने के बाद काम को फिर से करता है। यह बेकार लग सकता है, लेकिन अक्सर आप केवल Iterable को एक बार भी कॉल करेंगे, और भले ही आप इसे कई बार कॉल करते हों, फिर भी इसका उचित अर्थ है टी विफल यदि दो बार इस्तेमाल किया जाता है)।

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

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

यह तो इस तरह कोड में इस्तेमाल किया जा सकता है:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

जो मुझे वृद्धिशील मेमोरी का उपयोग करते हुए लूप के लिए कॉम्पैक्ट का उपयोग करने देता है।

यह दृष्टिकोण "आलसी" है - जब Iterable का अनुरोध किया जाता है, तो काम नहीं किया जाता है, लेकिन बाद में जब सामग्री को पुनरावृत्त किया जाता है - और आपको इसके परिणामों के बारे में पता होना चाहिए। एक DAO के साथ उदाहरण में जो डेटाबेस लेनदेन के भीतर परिणामों पर पुनरावृत्ति करता है।

इसलिए विभिन्न कैविटीज़ हैं, लेकिन यह अभी भी कई मामलों में एक उपयोगी मुहावरा हो सकता है।


अच्छा है, लेकिन अपने returning a fresh Iterator on each callसाथ क्यों होना चाहिए यानी संगामिति के मुद्दे को रोकने के लिए ...
अनिरुद्ध

7

अविश्वसनीय रूप से, किसी और ने अभी तक यह जवाब नहीं दिया है। यहां बताया गया है कि Iteratorनए जावा 8 Iterator.forEachRemaining()विधि का उपयोग करके आप "आसानी से" कैसे कर सकते हैं :

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

बेशक, वहाँ एक "सरल" समाधान है कि सीधे foreach पाश के साथ काम करता है, लपेटकर है Iteratorएक में Iterableलैम्ब्डा:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

5

Iteratorएक इंटरफ़ेस है जो आपको किसी चीज़ पर पुनरावृति करने की अनुमति देता है। यह किसी प्रकार के संग्रह के माध्यम से आगे बढ़ने का एक कार्यान्वयन है।

Iterable एक कार्यात्मक इंटरफ़ेस है जो यह दर्शाता है कि कुछ में एक सुलभ इट्रेटर है।

Java8 में, यह जीवन को बहुत आसान बना देता है ... यदि आपके पास एक की Iteratorआवश्यकता है Iterableतो आप बस कर सकते हैं:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

यह भी लूप के लिए काम करता है:

for (T item : ()->someIterator){
    //doSomething with item
}

2

मैं स्वीकृत उत्तर से सहमत हूं, लेकिन मैं अपना स्पष्टीकरण जोड़ना चाहता हूं।

  • इटरेटर ट्रैवर्सिंग की स्थिति का प्रतिनिधित्व करता है, उदाहरण के लिए, आप एक पुनरावृत्त से वर्तमान तत्व प्राप्त कर सकते हैं और अगले पर आगे बढ़ सकते हैं।

  • Iterable एक संग्रह का प्रतिनिधित्व करता है, जिसे ट्रैवर्स किया जा सकता है, यह जितने चाहें उतने पुनरावृत्तियों पर वापस आ सकता है, प्रत्येक प्रत्येक ट्रैवर्सिंग की अपनी स्थिति का प्रतिनिधित्व करता है, एक पुनरावृत्त पहले तत्व की ओर इशारा कर सकता है, जबकि दूसरा 3 तत्व की ओर इशारा कर सकता है।

यह अच्छा होगा यदि जावा लूप के लिए Iterator और Iterable दोनों को स्वीकार करता है।


2

java.utilपैकेज पर निर्भरता से बचने के लिए

मूल JSR के अनुसार, जावा ™ प्रोग्रामिंग लैंग्वेज के लिए एन एन्हांस्ड लूप , प्रस्तावित इंटरफेस:

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator
    (प्रस्तावित पर वापस लिया जाना प्रस्तावित है java.util.Iterator, लेकिन जाहिर तौर पर ऐसा कभी नहीं हुआ)

... के java.langबजाय पैकेज नाम स्थान का उपयोग करने के लिए डिज़ाइन किए गए थे java.util

JSR उद्धृत करने के लिए:

ये नए इंटरफेस भाषा पर निर्भरता को रोकने के लिए कार्य करते हैं।


वैसे, लैम्ब्डा सिंटैक्स (पासिंग ) के साथ उपयोग के लिए पुराने java.util.Iterableने forEachजावा 8+ में एक नया तरीका प्राप्त किया Consumer

यहाँ एक उदाहरण है। Listइंटरफ़ेस का विस्तार Iterableइंटरफेस, के रूप में किसी भी सूची एक वहन करती है forEachविधि।

List
.of ( "dog" , "cat" , "bird" )
.forEach ( ( String animal ) -> System.out.println ( "animal = " + animal ) );

2

मैं भी ऐसा करते हुए देख रहा हूँ:

public Iterator iterator() {
    return this;
}

लेकिन यह सही नहीं है! यह विधि वह नहीं होगी जो आप चाहते हैं!

विधि iterator()खरोंच से शुरू होने वाले एक नए पुनरावृत्तिकर्ता को वापस करना है। तो इस तरह से कुछ करने की जरूरत है:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(SomeType initdata)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new IterableIterator(this.intidata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

सवाल यह है कि क्या एक अमूर्त वर्ग को यह प्रदर्शन करने का कोई तरीका होगा? ताकि एक IterableIterator प्राप्त करने के लिए केवल एक ही दो तरीकों को लागू करने की आवश्यकता है () और hasNext ()


1

यदि आप वर्कअराउंड की तलाश में यहां आए हैं, तो आप IteratorIterable का उपयोग कर सकते हैं । (जावा 1.6 और इसके बाद के संस्करण के लिए उपलब्ध)

उदाहरण उपयोग (एक वेक्टर को उलट देना)।

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

प्रिंट

one
two
two
one

0

सादगी के लिए, Iterator और Iterable दो अलग-अलग अवधारणाएं हैं, Iterable केवल "I Iterator को वापस कर सकता है" के लिए एक शॉर्टहैंड है। मुझे लगता है कि आपका कोड होना चाहिए:

for(Object o : someContainer) {
}

someContainer Instof के साथ SomeContainer extends Iterable<Object>



0

संबंधित नोट पर, आपको Apache Commons Collections4 उपयोगी IteratorIterable एडेप्टर मिल सकता है। बस एक पुनरावृत्त से एक उदाहरण बनाएँ, और आपके पास इसके चलने योग्य है।

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

आईडी: org.apache.commons: कॉमन्स-संग्रह 4: 4.0


0

Iterators स्टेटफुल हैं, उनके पास एक "अगला" तत्व है और एक बार ओवररेटेड होने पर "थकावट" हो जाता है। यह देखने के लिए कि समस्या कहाँ है, निम्न कोड चलाएँ, कितनी संख्याएँ मुद्रित हैं?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

-1

आप निम्न उदाहरण की कोशिश कर सकते हैं:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.