Ich habe eine Drittanbieter-Bibliothek, die mir eine gibt Enumeration<String>
. Ich möchte mit dieser Aufzählung lazily als Java - 8 arbeiten Stream
, rufen Dinge wie filter
, map
und flatMap
auf sie.
Gibt es eine vorhandene Bibliothek, in der dies enthalten ist? Ich beziehe mich bereits auf Guava und Apache Commons. Wenn also einer von beiden die Lösung hat, wäre das ideal.
Alternativ, was ist der beste / einfachste Weg, um aus einer Zeit Enumeration
eine Stream
Faulheit zu machen?
java
java-8
java-stream
Micah Zoltu
quelle
quelle
Enumeration
(Java 1.0) einIterator
(Java 1.2) wird. Ich frage, wie man daraus einStream
(Java 1.8) macht. Während es so aussieht, als ob die letzte Antwort in der verknüpften Frage dies beantwortet, ist diese Antwort für die gestellte Frage falsch . Diese Antwort sollte hier angegeben werden, damit zukünftige Suchende sie erfolgreich finden können. Vielleicht möchte @ArneBurmeister die Antwort hier kopieren, damit diese Frage direkt beantwortet wird?Stream
(da dies nicht der Umfang der verknüpften Frage ist).Antworten:
Diese Antwort bietet bereits eine Lösung, die
Stream
aus einemEnumeration
:Es sollte betont werden , dass die resultierende
Stream
ist wie jede andere als faulStream
, da es keine Produkte verarbeiten , bevor das Terminal Aktion begonnen wurde , und wenn die Terminalbetrieb Kurzschluss ist, wird es nur so viele Punkte wie nötig wiederholen.Dennoch gibt es Raum für Verbesserungen. Ich würde immer eine
forEachRemaining
Methode hinzufügen , wenn es eine einfache Möglichkeit gibt, alle Elemente zu verarbeiten. Diese Methode wird von derStream
Implementierung für die meisten nicht kurzgeschlossenen Operationen aufgerufen :public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) { return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<T>() { public T next() { return e.nextElement(); } public boolean hasNext() { return e.hasMoreElements(); } public void forEachRemaining(Consumer<? super T> action) { while(e.hasMoreElements()) action.accept(e.nextElement()); } }, Spliterator.ORDERED), false); }
Der obige Code ist jedoch ein Opfer des
Iterator
Antipatterns "Verwenden, weil es so vertraut ist". Das erstellteIterator
wird in eine Implementierung der neuenSpliterator
Schnittstelle eingebunden und bietet keinen Vorteil gegenüber derSpliterator
direkten Implementierung :public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) { return StreamSupport.stream( new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) { public boolean tryAdvance(Consumer<? super T> action) { if(e.hasMoreElements()) { action.accept(e.nextElement()); return true; } return false; } public void forEachRemaining(Consumer<? super T> action) { while(e.hasMoreElements()) action.accept(e.nextElement()); } }, false); }
Auf der Quellcode-Ebene ist diese Implementierung so einfach wie die
Iterator
-basierte, eliminiert jedoch die Delegierung von aSpliterator
nach aIterator
. Die Leser müssen lediglich die neue API kennenlernen.quelle
ORDERED
bedeutet , dass es eine definierte Begegnung Ordnung, so bedeutet dies , dass die Stream - Implementierung ist nicht Optimierungen zu tun erlaubt , basierend auf die Daten unter der Annahme , ungeordnete zu sein. DaEnumeration
wir für ein Unbekanntes nicht wissen, ob die Reihenfolge eine Bedeutung hat, müssen wir davon ausgehen, dass sie eine Bedeutung haben könnte , und dieses Merkmal spezifizieren. Der Anrufer kannunordered()
den Stream weiterhin aufrufen, wenn er weiß, dass die Reihenfolge für die jeweiligen Daten irrelevant ist, um Optimierungen zu ermöglichen. Unsere anfängliche Prämisse muss jedoch sein, dass die Reihenfolge wichtig sein könnte.Enumeration#asIterator()
seit Java 9.Warum nicht Vanille Java verwenden:
Wie von @MicahZoltu erwähnt, muss jedoch die Anzahl der Elemente in der Aufzählung berücksichtigt werden, da
Collections.list
zuerst die Aufzählung durchlaufen wird, um die Elemente in eine zu kopierenArrayList
. Von dort aus kann die regulärestream
Methode angewendet werden. Während dies für viele Sammlungsstromoperationen üblich ist, kann dies zu Problemen führen, wenn die Aufzählung zu groß ist (wie unendlich), da die Aufzählung in eine Liste transformiert werden muss. Stattdessen sollten stattdessen die anderen hier beschriebenen Ansätze verwendet werden.quelle
enumeration
, um daraus eine Liste zu erstellen, und Sie erhalten dann Stream-Zugriff auf die Liste. Wenn die Aufzählung klein ist, ist dies wahrscheinlich ein vernünftiger Ansatz. Wenn die Aufzählung sehr groß ist, kann dies eine teure und unnötige Operation sein. Wenn die Aufzählung unendlich ist, stürzt Ihre Anwendung ab.In Java 9 ist es möglich, ein mit einem Einzeiler
Enumeration
in ein zu konvertierenStream
:Enumeration<String> en = ... ; Stream<String> str = StreamSupport.stream( Spliterators.spliteratorUnknownSize(en.asIterator(), Spliterator.ORDERED), false );
(Nun, es ist eine ziemlich lange Schlange.)
Wenn Sie nicht mit Java 9 arbeiten, können Sie das manuell mithilfe der in Holgers Antwort angegebenen Technik
Enumeration
in eine konvertieren .Iterator
quelle
Laut Guava-Dokumenten können Sie die folgende
Iterators.forEnumeration()
Methode verwenden:In dieser Frage wird erklärt, wie Sie einen Stream von einem Iterator erhalten:
Stream<Something> stream = StreamSupport.stream( Spliterators.spliteratorUnknownSize( iterator, Spliterator.ORDERED), false);
quelle
In meiner StreamEx- Bibliothek gibt es eine einfache Methode,
StreamEx.of(Enumeration)
die den Job erledigt:Beachten Sie, dass dies nicht nur eine Verknüpfung zur @ Holger-Lösung ist, sondern auf unterschiedliche Weise implementiert wird. Insbesondere weist es im Vergleich zu Lösungen, an denen beteiligt ist, signifikant bessere parallele Ausführungseigenschaften auf
Spliterators.spliteratorUnknownSize()
.quelle