it-swarm.com.de

Warum implementiert Stream <T> Iterable <T> nicht?

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?

237
roim

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ährend Stream etwas ist, das nur einmal verwendet werden kann - eher wie eine Iterator.

Wenn Stream erweitert Iterable, könnte der vorhandene Code überrascht sein, wenn er eine Iterable empfängt, die eine Exception die .__ auslöst. Zum zweiten Mal machen sie for (element : iterable).

176
kennytm

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 );
140
ZhongYu

Ich möchte darauf hinweisen, dass StreamExIterable (und Stream) implementiert sowie einen Host mit einer anderen immens fantastischen Funktionalität, die Stream fehlt.

10

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.

6
Zenexer

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.)

3
Rich

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);
    }
2
Ashish Tyagi

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]

2
John McClean

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.

0
yegor256

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();

    // ...
}
0
BullyWiiPlaza