Konvertieren Sie Iterable mit Java 8 JDK in Stream

418

Ich habe eine Schnittstelle, die zurückkehrt java.lang.Iterable<T>.

Ich möchte dieses Ergebnis mit der Java 8 Stream-API bearbeiten.

Iterable kann jedoch nicht "streamen".

Haben Sie eine Idee, wie Sie das Iterable als Stream verwenden können, ohne es in List zu konvertieren?

Rayman
quelle
2
Wenn Sie iterieren können, warum nicht einfach eine Schleife verwenden, um ihren Zustand oder Wert zu überprüfen, oder was auch immer?
Afzaal Ahmad Zeeshan
26
@ AfzaalAhmadZeeshan, weil Streams viel besser sind
Sleiman Jneidi
1
Wie gesagt, ich muss einige Manipulationen an dieser Liste vornehmen (Filter, Mapping). Ich möchte die neue Java 8 JDK API -> Stream verwenden. aber Iterable ist nicht "SteamAble"
Rayman
Scheint komisch, dass es myIterable.stream()das nicht gibt!
Josh M.
7
@ Guillaume: Ja, aber Stream.of(iterable)produziert Stream<Iterable<Object>>.
Matthias Braun

Antworten:

571

Es gibt eine viel bessere Antwort als die spliteratorUnknownSizedirekte Verwendung , was sowohl einfacher ist als auch ein besseres Ergebnis liefert. Iterablehat eine spliterator()Methode, also sollten Sie diese nur verwenden, um Ihren Spliterator zu erhalten. Im schlimmsten Fall handelt es sich um denselben Code (der von der Standardimplementierung verwendet wird spliteratorUnknownSize). Im häufigeren Fall, wenn Iterablees sich bereits um eine Sammlung handelt, erhalten Sie einen besseren Spliterator und damit eine bessere Stream-Leistung (möglicherweise sogar eine gute Parallelität). Es ist auch weniger Code:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

Wie Sie sehen können, ist es nicht sehr schmerzhaft , einen Stream von einem Iterable(siehe auch diese Frage ) zu erhalten.

Brian Goetz
quelle
109
Eine statische Methode Streamwäre schön gewesen, zB Stream.ofIterable(iterable).
Robinst
59
@robinst Dies war keine Auslassung; Es war eine bewusste (und umstrittene) Entscheidung. Die Herausforderung besteht darin, dass es einfach zu leicht wäre, dies zu erreichen, ohne die damit verbundenen Kompromisse zu verstehen. Da Iterable so abstrakt ist, würde eine solche Methode zu einem Stream mit der schlechtesten Leistung führen (keine parallele Unterstützung, keine Größeninformationen oder Merkmale (zur Optimierung der Ausführungsauswahl)). Das Erzwingen von mehr Gedanken führt zu besseren APIs im gesamten Ökosystem. Dies war ein Kompromiss zwischen "Was ist das Beste für XYZ-Code?" Und "Was ist das Beste für den gesamten Java-Code?".
Brian Goetz
2
Aufgrund Ihrer Erklärung bin ich gespannt, warum wir Collection.stream (), aber nicht Iterable.stream () erhalten haben. Es scheint, dass die Argumentation, Iterable.stream () (oder Stream.ofIterable ()) wegzulassen, gleichermaßen für Collection.stream () gilt.
Qualidafial
6
@qualidafial Siehe stackoverflow.com/questions/23114015/… .
Brian Goetz
11
@BrianGoetz Es sieht so aus, als ob die meisten Leute nicht in der Lage sind, das oben Gesagte zu verstehen oder sich nicht darum zu kümmern. Sie möchten den einfachen Code nur durch Aufrufen der einfachen API schreiben. Andererseits sind diese Dinge (parallel ...) für die meisten täglichen iterierbaren Operationen möglicherweise nicht wichtig.
user_3380739
71

Wenn Sie die Guava-Bibliothek seit Version 21 verwenden können, können Sie sie verwenden

Streams.stream(iterable)
numéro6
quelle
2
Oder verwenden Sie, wenn Sie bei einer älteren Version stecken bleiben Lists.newArrayList(Iterable).
Jacob van Lingen
1
Die aktuelle Guava-Implementierung ist nicht schlechter als die akzeptierte Antwort: github.com/google/guava/blob/master/guava/src/com/google/common/…
Vadzim
23

Sie können ganz einfach ein Streamaus einem Iterableoder erstellen Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
quelle
2
Sie müssen diese Funktion einmal schreiben und dann einfach aufrufen. Warum wird stream(...)Ihr Code aufgerufen, um Ihren Code zu überladen?
Gexizid
1
Wollte es tun Inline kurz und elegant .. Sie haben Recht, ich kann diese Funktion einmal schreiben .. aber ich bin in das Löschen von Code (und nicht das Hinzufügen von Code). Trotzdem ist diese Antwort richtig, denn so kann man das umwandeln.
Rayman
statischer Import der Funktion. kurz und elegant. (obwohl nicht unbedingt transparent)
aepurniet
9

Ich möchte vorschlagen, die JOOL- Bibliothek zu verwenden, die die Spliterator-Magie hinter dem (iterierbaren) Seq.seq-Aufruf verbirgt und eine ganze Reihe zusätzlicher nützlicher Funktionen bietet.

Shaggie
quelle
7

Wie in einer anderen Antwort erwähnt, unterstützt Guava dies durch die Verwendung von:

Streams.stream(iterable);

Ich möchte hervorheben, dass die Implementierung etwas anderes macht als andere vorgeschlagene Antworten. Wenn das Iterablevom Typ ist Collection, werfen sie es.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}
Alex
quelle
5

Ich habe diese Klasse erstellt:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

Ich denke, es ist perfekt lesbar, weil Sie nicht an Spliteratoren und Boolesche Werte denken müssen (isParallel).

gt
quelle
4

Eine sehr einfache Lösung für dieses Problem besteht darin, eine Streamable<T>Schnittstellenerweiterung zu erstellen Iterable<T>, die eine default <T> stream()Methode enthält.

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Jetzt können alle Ihre Iterable<T>s trivial streambar gemacht werden, indem Sie sie implements Streamable<T>stattdessen deklarieren Iterable<T>.

OldCurmudgeon
quelle
0

Wenn Sie Vavr (früher bekannt als Javaslang) verwenden, kann dies so einfach sein wie:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
quelle
-1

Eine andere Möglichkeit, mit Java 8 und ohne externe Bibliotheken:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Greg Witczak
quelle