Warum wird die Iterator
Schnittstelle nicht erweitert Iterable
?
Die iterator()
Methode könnte einfach zurückkehren this
.
Ist es absichtlich oder nur ein Versehen der Java-Designer?
Es wäre praktisch, eine for-each-Schleife mit Iteratoren wie diesen verwenden zu können:
for(Object o : someContainer.listSomeObjects()) {
....
}
Dabei wird listSomeObjects()
ein Iterator zurückgegeben.
Antworten:
Weil ein Iterator im Allgemeinen auf eine einzelne Instanz in einer Sammlung verweist. Iterable impliziert, dass man einen Iterator von einem Objekt erhalten kann, um seine Elemente zu durchlaufen - und es besteht keine Notwendigkeit, über eine einzelne Instanz zu iterieren, was ein Iterator darstellt.
quelle
Ein Iterator ist zustandsbehaftet. Die Idee ist, dass Sie bei
Iterable.iterator()
zweimaligem Aufruf unabhängige Iteratoren erhalten - für die meisten Iterables jedenfalls. Dies wäre in Ihrem Szenario eindeutig nicht der Fall.Zum Beispiel kann ich normalerweise schreiben:
Das sollte die Sammlung zweimal drucken - aber mit Ihrem Schema würde die zweite Schleife immer sofort beendet.
quelle
iterator
das Ergebnis aufrufen und verwenden, muss es die Sammlung durchlaufen - was nicht der Fall ist, wenn dasselbe Objekt bereits die Sammlung durchlaufen hat. Können Sie eine korrekte Implementierung angeben (außer für eine leere Sammlung), bei der derselbe Iterator zweimal zurückgegeben wird?iterable
zweimaliges Aufrufen Ihnen unabhängige Iteratoren gibt.Für meine $ 0.02 stimme ich voll und ganz zu, dass Iterator Iterable nicht implementieren sollte, aber ich denke, dass die erweiterte for-Schleife dies auch akzeptieren sollte. Ich denke, das ganze Argument "Iteratoren iterierbar machen" kommt als Umgehung eines Sprachfehlers auf.
Der ganze Grund für die Einführung der erweiterten for-Schleife war, dass sie "die Plackerei und Fehleranfälligkeit von Iteratoren und Indexvariablen beim Iterieren über Sammlungen und Arrays beseitigt" [ 1 ].
Warum gilt dasselbe Argument dann nicht für Iteratoren?
In beiden Fällen wurden die Aufrufe von hasNext () und next () entfernt, und es gibt keinen Verweis auf den Iterator in der inneren Schleife. Ja, ich verstehe, dass Iterables wiederverwendet werden können, um mehrere Iteratoren zu erstellen, aber dass alles außerhalb der for-Schleife geschieht: Innerhalb der Schleife gibt es immer nur einen Vorwärtsfortschritt um jeweils ein Element über die vom Iterator zurückgegebenen Elemente.
Wenn Sie dies zulassen, wird es auch einfacher, die for-Schleife für Aufzählungen zu verwenden, die, wie an anderer Stelle erwähnt, analog zu Iteratoren und nicht zu Iterablen ist.
Lassen Sie Iterator also nicht Iterable implementieren, sondern aktualisieren Sie die for-Schleife, um sie zu akzeptieren.
Prost,
quelle
for(item : iter) {...}
Syntax heraufstufen, wird ein Fehler ausgelöst, wenn derselbe Iterator zweimal iteriert wird. Stellen Sie sich vor , dieIterator
in übergeben wirditerateOver
Verfahren stattIterable
in diesem Beispiel .for (String x : strings) {...}
oder verwendenwhile (strings.hasNext()) {...}
: Wenn Sie versuchen, einen Iterator zweimal beim zweiten Mal zu durchlaufen, werden keine Ergebnisse erzielt. Ich sehe dies an sich nicht als Argument gegen das Zulassen der erweiterten Syntax. Jons Antwort ist anders, weil er dort zeigt, wie das Einwickeln einesIterator
in einIterable
Problem verursachen würde, da Sie in diesem Fall erwarten würden, dass Sie es so oft wiederverwenden können, wie Sie möchten.Wie von anderen betont,
Iterator
undIterable
sind zwei verschiedene Dinge.Außerdem sind
Iterator
Implementierungen älter als Schleifen.Es ist auch trivial, diese Einschränkung mit einer einfachen Adaptermethode zu überwinden, die bei Importen statischer Methoden folgendermaßen aussieht:
Beispielimplementierung:
In Java 8 wird das Anpassen von an
Iterator
anIterable
einfacher:quelle
for (String s : (Iterable<String>) () -> iterator)
Wie andere gesagt haben, kann ein Iterable mehrmals aufgerufen werden, wobei bei jedem Aufruf ein neuer Iterator zurückgegeben wird. Ein Iterator wird nur einmal verwendet. Sie sind also verwandt, dienen aber unterschiedlichen Zwecken. Frustrierenderweise funktioniert die "compact for" -Methode jedoch nur mit einer iterierbaren Methode.
Was ich im Folgenden beschreiben werde, ist eine Möglichkeit, das Beste aus beiden Welten zu haben - die Rückgabe einer Iterable (für eine bessere Syntax), selbst wenn die zugrunde liegende Datenfolge einmalig ist.
Der Trick besteht darin, eine anonyme Implementierung des Iterable zurückzugeben, die die Arbeit tatsächlich auslöst. Anstatt also die Arbeit zu erledigen, die eine einmalige Sequenz generiert, und dann einen Iterator darüber zurückzugeben, geben Sie eine Iterable zurück, die bei jedem Zugriff die Arbeit wiederholt. Das mag verschwenderisch erscheinen, aber oft wird das Iterable ohnehin nur einmal aufgerufen, und selbst wenn Sie es mehrmals aufrufen, hat es dennoch eine vernünftige Semantik (im Gegensatz zu einem einfachen Wrapper, der einen Iterator wie ein Iterable "aussehen" lässt, hat dies gewonnen). t bei zweimaliger Verwendung fehlschlagen).
Angenommen, ich habe ein DAO, das eine Reihe von Objekten aus einer Datenbank bereitstellt, und ich möchte über einen Iterator Zugriff darauf gewähren (z. B. um zu vermeiden, dass alle Objekte im Speicher erstellt werden, wenn sie nicht benötigt werden). Jetzt könnte ich einfach einen Iterator zurückgeben, aber das macht die Verwendung des zurückgegebenen Werts in einer Schleife hässlich. Also wickle ich stattdessen alles in eine anon Iterable:
Dies kann dann in Code wie folgt verwendet werden:
Dadurch kann ich die kompakte for-Schleife verwenden und gleichzeitig die inkrementelle Speichernutzung beibehalten.
Dieser Ansatz ist "faul" - die Arbeit wird nicht erledigt, wenn die Iterable angefordert wird, sondern erst später, wenn der Inhalt wiederholt wird - und Sie müssen sich der Konsequenzen bewusst sein. Im Beispiel mit einem DAO bedeutet dies, dass die Ergebnisse innerhalb der Datenbanktransaktion durchlaufen werden.
Es gibt also verschiedene Vorbehalte, aber dies kann in vielen Fällen immer noch eine nützliche Redewendung sein.
quelle
returning a fresh Iterator on each call
sollten mit warum begleitet werden, dh um Parallelitätsprobleme zu verhindern ...Unglaublicherweise hat noch niemand diese Antwort gegeben. So können Sie
Iterator
mit der neuen Java 8-Iterator.forEachRemaining()
Methode "einfach" über eine iterieren :Natürlich gibt es eine "einfachere" Lösung, die direkt mit der foreach-Schleife zusammenarbeitet und diese
Iterator
in einIterable
Lambda einwickelt :quelle
Iterator
ist eine Schnittstelle, mit der Sie über etwas iterieren können. Es ist eine Implementierung des Durchlaufens einer Sammlung.Iterable
ist eine funktionale Schnittstelle, die angibt, dass etwas einen zugänglichen Iterator enthält.In Java8 macht dies das Leben ziemlich einfach ... Wenn Sie eine haben,
Iterator
aber eine brauchenIterable
, können Sie einfach Folgendes tun:Dies funktioniert auch in der for-Schleife:
quelle
Ich stimme der akzeptierten Antwort zu, möchte aber meine eigene Erklärung hinzufügen.
Iterator stellt den Zustand des Durchlaufens dar. Sie können beispielsweise das aktuelle Element von einem Iterator abrufen und zum nächsten übergehen.
Iterable stellt eine Sammlung dar, die durchlaufen werden kann. Es kann so viele Iteratoren zurückgeben, wie Sie möchten. Jeder repräsentiert seinen eigenen Durchlaufzustand. Ein Iterator zeigt möglicherweise auf das erste Element, während ein anderer auf das dritte Element zeigt.
Es wäre schön, wenn Java for loop sowohl Iterator als auch Iterable akzeptiert.
quelle
Um die Abhängigkeit vom
java.util
Paket zu vermeidenLaut dem ursprünglichen JSR, einer erweiterten for-Schleife für die Java ™ -Programmiersprache , sind die vorgeschlagenen Schnittstellen:
java.lang.Iterable
java.lang.ReadOnlyIterator
(vorgeschlagen, nachgerüstet zu werden
java.util.Iterator
, aber anscheinend ist dies nie passiert)… Wurden entwickelt, um den
java.lang
Paket-Namespace anstelle von zu verwendenjava.util
.So zitieren Sie den JSR:
Übrigens hat das Alte in Java 8+
java.util.Iterable
eine neueforEach
Methode zur Verwendung mit Lambda-Syntax (Übergabe von aConsumer
) erhalten.Hier ist ein Beispiel. Die
List
Schnittstelle erweitert dieIterable
Schnittstelle, da jede Liste eineforEach
Methode enthält.quelle
Ich sehe auch viele, die dies tun:
Aber das macht es nicht richtig! Diese Methode wäre nicht das, was Sie wollen!
Die Methode
iterator()
soll einen neuen Iterator zurückgeben, der von vorne beginnt. Man muss also so etwas tun:Die Frage ist: Gibt es eine Möglichkeit, eine abstrakte Klasse dazu zu bringen, dies auszuführen? Um einen IterableIterator zu erhalten, müssen nur die beiden Methoden next () und hasNext () implementiert werden.
quelle
Wenn Sie auf der Suche nach einer Problemumgehung hierher gekommen sind, können Sie IteratorIterable verwenden . (verfügbar für Java 1.6 und höher)
Beispiel für die Verwendung (Umkehren eines Vektors).
druckt
quelle
Der Einfachheit halber sind Iterator und Iterable zwei unterschiedliche Konzepte. Iterable ist einfach eine Abkürzung für "Ich kann einen Iterator zurückgeben". Ich denke, dass Ihr Code sein sollte:
mit someContainer Instanz von
SomeContainer extends Iterable<Object>
quelle
Nebenbei: Scala hat eine toIterable () -Methode in Iterator. Siehe Scala implizite oder explizite Konvertierung von Iterator zu iterierbar
quelle
In einem verwandten Hinweis ist der IteratorIterable-Adapter in Apache Commons Collections4 möglicherweise hilfreich. Erstellen Sie einfach eine Instanz aus einem Iterator, und Sie haben die entsprechende Iterable.
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html
ID: org.apache.commons: commons-collection4: 4.0
quelle
Iteratoren sind zustandsbehaftet, sie haben ein "nächstes" Element und werden "erschöpft", sobald sie wiederholt werden. Führen Sie den folgenden Code aus, um festzustellen, wo das Problem liegt. Wie viele Zahlen werden gedruckt?
quelle
Sie können das folgende Beispiel ausprobieren:
quelle