Considere una List<String> stringListque se puede imprimir de muchas maneras usando construcciones Java 8 :
stringList.forEach(System.out::println); // 1) Iterable.forEach
stringList.stream().forEach(System.out::println); // 2) Stream.forEach (order maintained generally but doc does not guarantee)
stringList.stream().forEachOrdered(System.out::println); // 3) Stream.forEachOrdered (order maintained always)
stringList.parallelStream().forEach(System.out::println); // 4) Parallel version of Stream.forEach (order not maintained)
stringList.parallelStream().forEachOrdered(System.out::println); // 5) Parallel version ofStream.forEachOrdered (order maintained always)
¿En qué se diferencian estos enfoques entre sí?
Primer enfoque ( Iterable.forEach):
el iterador de la colección generalmente se usa y está diseñado para fallar rápidamente, lo que significa que se lanzará ConcurrentModificationExceptionsi la colección subyacente se modifica estructuralmente durante la iteración. Como se menciona en el documento para ArrayList:
Una modificación estructural es cualquier operación que agrega o elimina uno o más elementos, o cambia el tamaño explícitamente de la matriz de respaldo; simplemente establecer el valor de un elemento no es una modificación estructural.
Por lo tanto, significa ArrayList.forEachque el valor está permitido sin ningún problema. Y en el caso de una recopilación simultánea, por ejemplo, ConcurrentLinkedQueueel iterador sería débilmente consistente, lo que significa que las acciones aprobadas forEachpueden realizar incluso cambios estructurales sin que se produzca una ConcurrentModificationExceptionexcepción. Pero aquí las modificaciones pueden o no ser visibles en esa iteración.
Segundo enfoque ( Stream.forEach):
el orden no está definido. Aunque puede que no ocurra para secuencias secuenciales, la especificación no lo garantiza. También se requiere que la acción sea de naturaleza no interferente. Como se menciona en el documento :
El comportamiento de esta operación es explícitamente no determinista. Para las tuberías de flujo paralelo, esta operación no garantiza el respeto del orden de encuentro del flujo, ya que hacerlo sacrificaría el beneficio del paralelismo.
Tercer enfoque ( Stream.forEachOrdered):
la acción se realizaría en el orden de encuentro de la secuencia. Por lo tanto, cuando el orden importa se usa forEachOrderedsin pensarlo dos veces. Como se menciona en el documento :
Realiza una acción para cada elemento de esta secuencia, en el orden de encuentro de la secuencia si la secuencia tiene un orden de encuentro definido.
Mientras itera sobre una colección sincronizada, el primer enfoque tomaría el bloqueo de la colección una vez y lo mantendría en todos los métodos de llamadas a la acción, pero en el caso de las transmisiones, usan el spliterator de la colección, que no se bloquea y se basa en las reglas establecidas de no -interferencia. En caso de que la copia de respaldo de la secuencia se modifique durante la iteración, unConcurrentModificationException , se generará un resultado incoherente.
Cuarto enfoque (paralelo Stream.forEach):
como ya se mencionó, no hay garantía de respetar el orden de encuentro como se esperaba en el caso de flujos paralelos. Es posible que la acción se realice en diferentes hilos para diferentes elementos que nunca pueden ser el caso forEachOrdered.
Quinta Approach (Parallel Stream.forEachOrdered) -
El forEachOrderedprocesará los elementos en el orden especificado por la fuente con independencia del hecho de si la corriente es secuencial o en paralelo. Por lo tanto, no tiene sentido usar esto con flujos paralelos.
Listser? Muéstranos cómo lo declaraste y lo instanciaste.