Saltar al contenido

¿Cómo imprimir todos los elementos de una lista en Java?

octubre 17, 2021
apple touch icon@2

Considere un List<String> stringList que se puede imprimir de muchas formas usando Java 8 constructos:

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 se usa generalmente y está diseñado para ser Fallar rapido lo que significa que arrojará ConcurrentModificationException si la colección subyacente es estructuralmente modificado durante la iteración. Como se menciona en el Doc por ArrayList:

Una modificación estructural es cualquier operación que agrega o elimina uno o más elementos, o cambia explícitamente el tamaño de la matriz de respaldo; simplemente establecer el valor de un elemento no es una modificación estructural.

Entonces significa para ArrayList.forEach la configuración del valor está permitida sin ningún problema. Y en caso de recolección concurrente, por ejemplo ConcurrentLinkedQueue el iterador sería débilmente consistente lo que significa que las acciones pasaron en forEach se les permite hacer incluso cambios estructurales sin ConcurrentModificationExceptionexcepción lanzada. Pero aquí las modificaciones pueden ser visibles o no en esa iteración.

Segundo enfoque (Stream.forEach) –
El orden no está definido. Aunque puede que no ocurra para transmisiones secuenciales, la especificación no lo garantiza. Además, la acción debe ser de naturaleza no interferente. Como se menciona en Doc:

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.

Tercer enfoque (Stream.forEachOrdered) –
La acción se realizaría en el orden de encuentro de la secuencia. Entonces, siempre que el orden importe, use forEachOrdered Sin pensarlo dos veces. Como se menciona en el Doc:

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

Mientras itera sobre un colección sincronizada los primer enfoque tomaría el bloqueo de la colección una vez y lo mantendría en todas las llamadas al método de acción, pero en el caso de las transmisiones, utilizan el spliterator de la colección, que no se bloquea y se basa en las reglas de no interferencia ya establecidas. En caso de que el respaldo de la colección, la secuencia se modifique durante la iteración, ConcurrentModificationException se arrojaría o podría producirse un resultado inconsistente.

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 corrientes paralelas. Es posible que la acción se realice en diferentes hilos para diferentes elementos, lo que nunca puede ser el caso con forEachOrdered.

Quinto enfoque (paralelo Stream.forEachOrdered) –
los forEachOrdered procesará los elementos en el orden especificado por la fuente, independientemente de si el flujo es secuencial o paralelo. Por lo tanto, no tiene sentido usar esto con flujos paralelos.

close