Convertir Iterable a Stream usando Java 8 JDK

420

Tengo una interfaz que vuelve java.lang.Iterable<T>.

Me gustaría manipular ese resultado utilizando la API Java 8 Stream.

Sin embargo, Iterable no puede "transmitir".

¿Alguna idea de cómo usar Iterable como Stream sin convertirlo en List?

Rayman
fuente
2
Si puede iterar, ¿por qué no usar simplemente un bucle para verificar su condición o valor o lo que sea?
Afzaal Ahmad Zeeshan
26
@AfzaalAhmadZeeshan porque las transmisiones son mucho mejores
Sleiman Jneidi
1
Como dije, necesito hacer algunas manipulaciones en esa lista (filtros, mapeo). Me gustaría utilizar la nueva API Java 8 JDK -> Stream. pero Iterable no es "SteamAble"
rayman
Parece extraño que myIterable.stream()no existe!
Josh M.
77
@ Guillaume: Sí, pero Stream.of(iterable)produce Stream<Iterable<Object>>.
Matthias Braun

Respuestas:

573

Hay una respuesta mucho mejor que usar spliteratorUnknownSizedirectamente, lo cual es más fácil y obtiene un mejor resultado. Iterabletiene un spliterator()método, por lo que debe usarlo para obtener su spliterator. En el peor de los casos, es el mismo código (la implementación predeterminada lo usa spliteratorUnknownSize), pero en el caso más común, donde Iterableya es una colección, obtendrá un mejor spliterator y, por lo tanto, un mejor rendimiento de transmisión (tal vez incluso un buen paralelismo). También es menos código:

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

Como puede ver, obtener una transmisión de un Iterable(vea también esta pregunta ) no es muy doloroso.

Brian Goetz
fuente
109
Un método estático en Streamhabría sido bueno, por ejemplo Stream.ofIterable(iterable).
robinst
59
@robinst Esto no fue una omisión; fue una elección deliberada (y muy debatida). El desafío es que si esto existiera, sería demasiado fácil alcanzarlo sin comprender las compensaciones que conlleva. Debido a que Iterable es tan abstracto, dicho método daría como resultado el flujo de peor rendimiento posible (sin soporte paralelo, sin información de tamaño o características (utilizado para optimizar las opciones de ejecución)). Forzar más resultados de pensamiento en mejores API en todo el ecosistema. Esto fue una compensación de "lo mejor para el código XYZ" frente a "lo mejor para todo el código Java".
Brian Goetz el
2
Según su explicación, tengo curiosidad por saber por qué obtuvimos Collection.stream () pero no Iterable.stream (). Parece que el razonamiento para omitir Iterable.stream () (o Stream.ofIterable ()) se aplica igualmente a Collection.stream ().
qualidafial
66
@qualidafial Consulte stackoverflow.com/questions/23114015/… .
Brian Goetz
11
@BrianGoetz Parece que la mayoría de las personas no están en el nivel para entender lo que dijo anteriormente o no les importa. solo quieren escribir el código simple llamando a API simple. Por otro lado, esas cosas (paralelas ...) tal vez no sean importantes para la mayoría de las operaciones iterables diarias.
user_3380739
23

Puede crear fácilmente Streamun Iterableo Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
fuente
2
Tienes que escribir esta función una vez y luego simplemente llamarla. ¿Por qué una llamada para stream(...)saturar tu código?
Gexicida
1
Quería hacerlo en línea, corto y elegante ... tienes razón, puedo escribir esta función una vez ... pero me gusta soltar código (y no agregar código). de todos modos esta respuesta es correcta porque esa es la forma de convertir esto.
rayman
importación estática dicha función. Corto y elegante. (aunque no necesariamente transparente)
aepurniet
9

Me gustaría sugerir el uso de la biblioteca JOOL , oculta la magia de spliterator detrás de la llamada Seq.seq (iterable) y también proporciona un montón de funcionalidades adicionales útiles.

Shaggie
fuente
7

Entonces, como otra respuesta mencionó, Guava tiene soporte para esto al usar:

Streams.stream(iterable);

Quiero resaltar que la implementación hace algo ligeramente diferente a otras respuestas sugeridas. Si el Iterablees de tipo, Collectionlo echan.

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
fuente
5

He creado esta clase:

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

Creo que es perfectamente legible porque no tienes que pensar en spliterators y booleans (isParallel).

gt
fuente
4

Una solución muy simple para este problema es crear una Streamable<T>interfaz Iterable<T>que se extienda y que contenga un default <T> stream()método.

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

Ahora cualquiera de tus Iterable<T>correos electrónicos se puede hacer trivialmente en streaming simplemente declarándolos en implements Streamable<T>lugar de hacerlo Iterable<T>.

OldCurmudgeon
fuente
0

Si utiliza Vavr (anteriormente conocido como Javaslang), esto puede ser tan fácil como:

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

Otra forma de hacerlo, con Java 8 y sin bibliotecas externas:

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