forEach vs forEachOrdered en Java 8 Stream

86

Entiendo que estos métodos difieren en el orden de ejecución, pero en todas mis pruebas no puedo lograr una ejecución de órdenes diferente.

Ejemplo:

System.out.println("forEach Demo");
Stream.of("AAA","BBB","CCC").forEach(s->System.out.println("Output:"+s));
System.out.println("forEachOrdered Demo");
Stream.of("AAA","BBB","CCC").forEachOrdered(s->System.out.println("Output:"+s));

Salida:

forEach Demo
Output:AAA
Output:BBB
Output:CCC
forEachOrdered Demo
Output:AAA
Output:BBB
Output:CCC

Proporcione ejemplos en los que dos métodos producirán resultados diferentes.

gstackoverflow
fuente
Pruebe tal vez con transmisiones paralelas.
Pshemo
@Pshemo ¿es la única opción posible?
gstackoverflow
5
Un orden no especificado no implica "garantizado que sea un orden diferente". Simplemente significa sin especificar , lo que siempre implica la posibilidad de coincidir con el orden de encuentro. No hay función de reproducción aleatoria incorporada.
Holger

Respuestas:

89
Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s));
Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s));

La segunda línea siempre saldrá

Output:AAA
Output:BBB
Output:CCC

mientras que el primero no está garantizado ya que no se mantiene el pedido. forEachOrderedprocesará los elementos de la secuencia en el orden especificado por su fuente, independientemente de si la secuencia es secuencial o paralela.

Citando de forEachJavadoc:

El comportamiento de esta operación es explícitamente no determinista. Para las tuberías de corrientes paralelas, esta operación no garantiza respetar el orden de encuentro de la corriente, ya que hacerlo sacrificaría el beneficio del paralelismo.

Cuando el forEachOrderedJavadoc dice (énfasis mío):

Realiza una acción para cada elemento de esta corriente, en el orden de encuentro de la corriente si la corriente tiene un orden de encuentro definido.

Atún
fuente
6
Sí, tiene usted razón. ¿Es posible solo para los flujos paralelos?
gstackoverflow
6
Incluso si solo se aplicaría a transmisiones paralelas en este momento, y no digo que lo haga, aún podría romperse en el futuro si algunos pasos intermedios se optimizan para aprovechar las transmisiones desordenadas, por ejemplo, una clasificación podría usar un algoritmo inestable si el arroyo está desordenado.
the8472
1
¿Entonces no tiene sentido usar forEachOrderedcon parallel?
Bhushan
3
@BhushanPatil Sí, eso es correcto. stackoverflow.com/questions/47336825/…
Sagar
1
El uso de forEachOrdered procesará el elemento por orden y luego el uso de flujos paralelos perderá los beneficios del paralelismo. Por favor recomiende.
Deepak
30

Aunque es forEachmás corto y se ve más bonito, sugeriría usarlo forEachOrdereden todos los lugares donde el orden importa para especificar esto explícitamente. Para las transmisiones secuenciales, forEachparece respetar el orden e incluso los usos del código interno de la API de transmisión forEach(para la transmisión que se sabe que es secuencial) donde es semánticamente necesario usar forEachOrdered. Sin embargo, más tarde puede decidir cambiar su flujo a paralelo y su código se romperá. También cuando usa forEachOrderedel lector de su código ve el mensaje: "el orden importa aquí". Por lo tanto, documenta mejor su código.

Tenga en cuenta también que para los flujos paralelos, forEachno solo se ejecuta en orden no determenístico, sino que también puede ejecutarlo simultáneamente en diferentes hilos para diferentes elementos (lo que no es posible con forEachOrdered).

Finalmente, ambos forEach/ forEachOrderedrara vez son útiles. En la mayoría de los casos, es necesario producir algún resultado, no solo un efecto secundario, por lo que las operaciones como reduceo collectdeberían ser más adecuadas. Expresar la operación de reducción por naturaleza a través de forEachgeneralmente se considera un mal estilo.

Tagir Valeev
fuente
7
"Finalmente, tanto forEach / forEachOrdered rara vez son útiles". No podría estar más de acuerdo. Parece que estos métodos se utilizan en exceso.
Tunaki
Gracias por responder. pero no es un ejemplo de la vida real. Acabo de aprender Java 8
gstackoverflow
¿Por qué es semánticamente necesario usar forEachOrdereden ese código?
RealSkeptic
1
@RealSkeptic, es una secuencia especificada por el usuario (pasada a flatMap). Puede solicitarse, por lo que debe colocarse en el flujo resultante en el mismo orden.
Tagir Valeev
2
@RealSkeptic, ¡eres el verdadero escéptico! Stream.of("a", "b", "c").flatMap(s -> Stream.of("1", "2", "3").map(s::concat)).spliterator().hasCharacteristics(Spliterator.ORDERED)devuelve verdadero, por lo que se debe determinar el orden de la secuencia resultante. Si cree que la documentación de JDK debería decir explícitamente sobre esto, no dude en enviar un error.
Tagir Valeev
15

forEach()El método realiza una acción para cada elemento de esta secuencia. Para flujo paralelo, esta operación no garantiza mantener el orden del flujo.

forEachOrdered() El método realiza una acción para cada elemento de este flujo, garantizando que cada elemento se procesa en orden de encuentro para los flujos que tienen un orden de encuentro definido.

tome el ejemplo siguiente:

    String str = "sushil mittal";
    System.out.println("****forEach without using parallel****");
    str.chars().forEach(s -> System.out.print((char) s));
    System.out.println("\n****forEach with using parallel****");

    str.chars().parallel().forEach(s -> System.out.print((char) s));
    System.out.println("\n****forEachOrdered with using parallel****");

    str.chars().parallel().forEachOrdered(s -> System.out.print((char) s));

Salida:

****forEach without using parallel****

sushil mittal

****forEach with using parallel****

mihul issltat

****forEachOrdered with using parallel****

sushil mittal
Sushil Mittal
fuente