In Java 8 haben wir die Klasse Stream <T> , die seltsamerweise eine Methode haben
Iterator<T> iterator()
Sie würden also die Schnittstelle Iterable <T> implementieren, die genau diese Methode erfordert, aber das ist nicht der Fall.
Wenn ich einen Stream mit einer foreach-Schleife durchlaufen möchte, muss ich Folgendes tun
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)) { ... }
Fehlt mir hier etwas?
Es gibt bereits Leute, die dasselbe auf der Mailingliste ☺ gefragt haben. Der Hauptgrund ist, dass Iterable auch eine wiederholbare Semantik hat, während Stream dies nicht tut.
Ich denke, der Hauptgrund ist, dass
Iterable
Wiederverwendbarkeit impliziert, währendStream
etwas ist, das nur einmal verwendet werden kann - eher wie eineIterator
.Wenn
Stream
erweitertIterable
, könnte der vorhandene Code überrascht sein, wenn er eineIterable
empfängt, die eineException
die .__ auslöst. Zum zweiten Mal machen siefor (element : iterable)
.
Um eine Stream
in eine Iterable
umzuwandeln, können Sie dies tun
Stream<X> stream = null;
Iterable<X> iterable = stream::iterator
Um eine Stream
an eine Methode zu übergeben, die Iterable
erwartet,
void foo(Iterable<X> iterable)
einfach
foo(stream::iterator)
aber es sieht wahrscheinlich komisch aus; Es könnte besser sein, etwas expliziter zu sein
foo( (Iterable<X>)stream::iterator );
Ich möchte darauf hinweisen, dass StreamEx
Iterable
(und Stream
) implementiert sowie einen Host mit einer anderen immens fantastischen Funktionalität, die Stream
fehlt.
kennytm beschrieb warum es nicht sicher ist, eine Stream
als Iterable
zu behandeln, und Zhong Yu bot eine Problemumgehung an die die Verwendung einer Stream
wie in Iterable
erlaubt, wenn auch auf unsichere Weise. Es ist möglich, das Beste aus beiden Welten herauszuholen: eine wiederverwendbare Iterable
von einer Stream
, die alle Garantien der Iterable
-Spezifikation erfüllt.
Hinweis: SomeType
ist hier kein Typparameter - Sie müssen ihn durch einen geeigneten Typ (z. B. String
) ersetzen oder auf Reflektion zurückgreifen.
Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):
Es gibt einen wesentlichen Nachteil:
Die Vorteile von Lazy Iteration gehen verloren. Wenn Sie vorhaben, alle Werte im aktuellen Thread sofort zu durchlaufen, ist der Overhead vernachlässigbar. Wenn Sie jedoch nur teilweise oder in einem anderen Thread iterieren wollten, könnte diese unmittelbare und vollständige Iteration unbeabsichtigte Folgen haben.
Der große Vorteil ist natürlich, dass Sie die Iterable
wiederverwenden können, während (Iterable<SomeType>) stream::iterator
nur eine einmalige Verwendung zulässt. Wenn der empfangende Code die Sammlung mehrmals durchläuft, ist dies nicht nur notwendig, sondern wahrscheinlich auch für die Leistung von Vorteil.
Sie können einen Stream in einer for
-Schleife wie folgt verwenden:
Stream<T> stream = ...;
for (T x : (Iterable<T>) stream::iterator) {
...
}
(Run dieses Snippet hier )
(Dies verwendet eine Java 8-Funktionsschnittstellen-Umwandlung.)
(Dies wird in einigen der obigen Kommentare behandelt (z. B. Aleksandr Dubinsky ), aber ich wollte es in eine Antwort ziehen, um es sichtbarer zu machen.)
Stream
implementiert Iterable
nicht. Das allgemeine Verständnis von Iterable
ist alles, worauf oft wiederholt werden kann. Stream
kann nicht wiedergegeben werden.
Die einzige Problemumgehung, die ich mir vorstellen kann, bei der eine iterable, die auf einem Stream basiert, auch wiedergegeben werden kann, ist das erneute Erstellen des Streams. Ich verwende unten eine Supplier
, um eine neue Instanz des Streams zu erstellen. Jedes Mal, wenn ein neuer Iterator erstellt wird.
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);
}
Wenn es Ihnen nichts ausmacht, die Bibliotheken von Drittanbietern zu verwenden cyclops-react definiert einen Stream, der sowohl Stream als auch Iterable implementiert und auch wiedergegeben werden kann (Lösung des Problems kennytm ).
Stream<String> stream = ReactiveSeq.of("hello","world")
.map(s->"prefix-"+s);
oder :-
Iterable<String> stream = ReactiveSeq.of("hello","world")
.map(s->"prefix-"+s);
stream.forEach(System.out::println);
stream.forEach(System.out::println);
[Offenlegung Ich bin der Hauptentwickler von Cyclops-React]
Nicht perfekt, wird aber funktionieren:
iterable = stream.collect(Collectors.toList());
Nicht perfekt, da alle Elemente aus dem Stream abgerufen und in List
eingefügt werden. Dies ist nicht genau das, was Iterable
und Stream
sind. Sie sollen faul sein.
Sie können alle Dateien in einem Ordner mithilfe von Stream<Path>
wie folgt durchlaufen:
Path path = Paths.get("...");
Stream<Path> files = Files.list(path);
for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
Object file = it.next();
// ...
}