Konwertuj Iterable na Stream za pomocą Java 8 JDK

418

Mam interfejs, który zwraca java.lang.Iterable<T>.

Chciałbym manipulować tym wynikiem przy użyciu interfejsu API Java 8 Stream.

Jednak Iterable nie może „przesyłać strumieniowo”.

Masz pomysł, jak używać Iterable jako strumienia bez konwertowania go na List?

rayman
źródło
2
Jeśli potrafisz iterować, dlaczego nie użyć pętli, aby sprawdzić jej stan lub wartość, czy co jeszcze?
Afzaal Ahmad Zeeshan
26
@AfzaalAhmadZeeshan, ponieważ strumienie są znacznie lepsze
Sleiman Jneidi
1
Jak powiedziałem, muszę wykonać kilka maniuplacji na tej liście (filtry, mapowanie). Chciałbym użyć nowego interfejsu API JDK Java 8 -> Stream. ale Iterable nie jest „SteamAble”
rayman
Dziwne, że myIterable.stream()nie istnieje!
Josh M.
7
@Guillaume: Tak, ale Stream.of(iterable)produkuje Stream<Iterable<Object>>.
Matthias Braun

Odpowiedzi:

571

Odpowiedź jest znacznie lepsza niż spliteratorUnknownSizebezpośrednie, co jest łatwiejsze i daje lepszy wynik. Iterablema spliterator()metodę, więc powinieneś jej użyć, aby uzyskać swój spliterator. W najgorszym przypadku jest to ten sam kod (używana jest implementacja domyślna spliteratorUnknownSize), ale w bardziej powszechnym przypadku, gdy Twoja Iterablekolekcja jest już zbiorem, otrzymasz lepszy spliterator, a zatem lepszą wydajność strumienia (może nawet dobrą równoległość). To także mniej kodu:

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

Jak widać, uzyskanie strumienia z Iterable(patrz także to pytanie ) nie jest bardzo bolesne.

Brian Goetz
źródło
109
Przydałaby się metoda statyczna Stream, np Stream.ofIterable(iterable).
robinst
59
@robinst To nie było pominięcie; był to celowy (i bardzo dyskutowany) wybór. Wyzwanie polega na tym, że gdyby to istniało, byłoby po prostu zbyt łatwo sięgnąć po nie rozumiejąc wynikających z tego kompromisów. Ponieważ Iterable jest tak abstrakcyjny, taka metoda spowodowałaby możliwie najgorzej działający strumień (brak wsparcia równoległego, brak informacji o wielkości lub cechach (wykorzystywanych do optymalizacji opcji wykonania). Wymuszanie większej ilości myśli powoduje lepsze interfejsy API w całym ekosystemie. Był to kompromis między „tym, co najlepsze w kodzie XYZ” a „tym, co najlepsze w całym kodzie Java”.
Brian Goetz
2
Na podstawie twojego wyjaśnienia jestem ciekawy, dlaczego mamy Collection.stream (), ale nie Iterable.stream (). Wydaje się, że uzasadnienie pominięcia Iterable.stream () (lub Stream.ofIterable ()) dotyczy w równym stopniu Collection.stream ().
Qualidafial
6
@qualidafial Zobacz stackoverflow.com/questions/23114015/... .
Brian Goetz
11
@BrianGoetz Wygląda na to, że większość ludzi nie jest na poziomie, aby zrozumieć, co powiedziałeś powyżej lub się tym nie przejmować. chcą tylko napisać prosty kod, wywołując prosty interfejs API. Z drugiej strony, te rzeczy (równolegle ...) mogą nie być ważne dla większości codziennych operacji iteracyjnych.
user_3380739,
71

Jeśli możesz korzystać z biblioteki Guava, od wersji 21 możesz używać

Streams.stream(iterable)
liczba6
źródło
2
Lub, jeśli utkniesz w starszej wersji, użyj Lists.newArrayList(Iterable).
Jacob van Lingen
1
Obecna implementacja Guawy
Vadzim
23

Można łatwo utworzyć StreamOut Of Iterablelub Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
źródło
2
Musisz napisać tę funkcję raz, a następnie po prostu ją wywołać. Dlaczego wezwanie do stream(...)zaśmiecania kodu?
zabójstwo
1
Chciałem to zrobić Inline, krótki i elegancki… masz rację. Mogę raz napisać tę funkcję… ale lubię upuszczać kod (i nie dodawać kodu). w każdym razie ta odpowiedź jest słuszna, ponieważ jest to sposób na konwersję tego.
rayman
import statyczny wspomnianej funkcji. krótki i elegancki. (choć niekoniecznie przezroczyste)
aepurniet
9

Chciałbym zasugerować użycie biblioteki JOOL , która ukrywa magię spliteratora za wywołaniem Seq.seq (iterable), a także zapewnia całą masę dodatkowych przydatnych funkcji.

Shaggie
źródło
7

Tak więc, jak wspomniano w innej odpowiedzi, Guava wspiera to za pomocą:

Streams.stream(iterable);

Chcę podkreślić, że implementacja robi coś nieco innego niż inne sugerowane odpowiedzi. Jeśli Iterablejest typu Collection, rzucają go.

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
źródło
5

Stworzyłem tę klasę:

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

Myślę, że jest to doskonale czytelne, ponieważ nie musisz myśleć o spliteratorach i booleanach (isParallel).

gt
źródło
4

Bardzo prostym obejściem tego problemu jest utworzenie Streamable<T>rozszerzenia interfejsu Iterable<T>zawierającego default <T> stream()metodę.

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

Teraz każdy z twoich Iterable<T>może być w prosty sposób udostępniony do streamingu, po prostu zadeklarując je implements Streamable<T>zamiast Iterable<T>.

OldCurmudgeon
źródło
0

Jeśli zdarzy ci się korzystać z Vavr (wcześniej znanego jako JavaScript), może to być tak proste, jak:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
źródło
-1

Innym sposobem na to, z Java 8 i bez zewnętrznych bibliotek:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Greg Witczak
źródło