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?


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.


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


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


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

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.


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

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


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


0

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

Iterable i = //...
Stream.ofAll(i);

-1

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

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.