¿Cómo asegurar el orden de procesamiento en las secuencias java8?

148

Quiero procesar listas dentro de un XMLobjeto java. Tengo que asegurarme de procesar todos los elementos para poder recibirlos.

¿Por lo tanto, debo recurrir sequentiala cada uno streamque uso? list.stream().sequential().filter().forEach()

¿O es suficiente usar la secuencia siempre que no use el paralelismo? list.stream().filter().forEach()

miembros
fuente

Respuestas:

339

Estás haciendo la pregunta equivocada. Usted está preguntando acerca de sequentialvs. parallelmientras que desea procesar los artículos en orden , por lo que debe preguntar sobre el pedido . Si tiene una secuencia ordenada y realiza operaciones que garantizan mantener la orden, no importa si la secuencia se procesa en paralelo o secuencial; La implementación mantendrá el orden.

La propiedad ordenada es distinta de paralela versus secuencial. Por ejemplo, si llama stream()a un HashSetflujo, el flujo no estará ordenado mientras llama stream()y Listdevuelve un flujo ordenado. Tenga en cuenta que puede llamar unordered()para liberar el contrato de pedido y potencialmente aumentar el rendimiento. Una vez que la secuencia no tiene orden, no hay forma de restablecer el orden. (La única forma de convertir una secuencia no ordenada en una ordenada es llamar sorted, sin embargo, la orden resultante no es necesariamente la orden original).

Consulte también la sección "Pedidos" de la java.util.streamdocumentación del paquete .

Para garantizar el mantenimiento del pedido a lo largo de una operación de flujo completo, debe estudiar la documentación de la fuente del flujo, todas las operaciones intermedias y la operación del terminal para saber si mantienen el pedido o no (o si la fuente tiene un pedido en el primer sitio).

Esto puede ser muy sutil, por ejemplo, Stream.iterate(T,UnaryOperator)crea una secuencia ordenada mientras Stream.generate(Supplier)crea una secuencia no ordenada . Tenga en cuenta que también cometió un error común en su pregunta, ya que no mantiene el orden. Debe usarlo si desea procesar los elementos de la secuencia en un orden garantizado.forEach forEachOrdered

Entonces si tu list pregunta es realmente un java.util.List, su stream()método devolverá una secuencia ordenada y filterno cambiará el orden. Entonces, si llama list.stream().filter() .forEachOrdered(), todos los elementos se procesarán secuencialmente en orden, mientras que para list.parallelStream().filter().forEachOrdered()los elementos podrían procesarse en paralelo (por ejemplo, por el filtro), pero la acción terminal aún se llamará en orden (lo que obviamente reducirá el beneficio de la ejecución en paralelo) .

Si, por ejemplo, usa una operación como

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

toda la operación podría beneficiarse de la ejecución paralela, pero la lista resultante siempre estará en el orden correcto, independientemente de si usa una secuencia paralela o secuencial.

Holger
fuente
48
Si, buena respuesta. Una cosa que he encontrado es que la terminología que usamos, al menos en inglés, como "antes", "después", etc., es bastante ambigua. Aquí hay dos tipos de ordenamiento: 1) orden de encuentro (también conocido como orden espacial ) y 2) orden de procesamiento (también conocido como orden temporal ). Con esta distinción en mente, puede ser útil usar palabras como "izquierda de" o "derecha de" cuando se habla del orden de encuentro y "antes de" o "más tarde que" cuando se habla del orden de procesamiento.
Stuart Marks
Entiendo List<>que preservará el orden, pero Collection<>¿lo hará ?
Josh C.
55
@JoshC. Depende del tipo de colección real. Sets generalmente no, a menos que sea un SortedSeto LinkedHashSet. Las vistas de recopilación de a Map( keySet(), entrySet()y values()) heredan la Mappolítica de 's, es decir, se ordenan cuando el mapa es un SortedMapo LinkedHashMap. El comportamiento está determinado por las características informadas por el spliterator de la colección . La defaultimplementación de Collectionno informa la ORDEREDcaracterística, por lo que está desordenada, a menos que se anule.
Holger
@Holger Tenía una pregunta que podría estar relacionada de alguna manera con una pequeña sección de su respuesta.
Naman
1
Vale la pena señalar que forEachOrderedsolo difiere forEachcuando se usan flujos paralelos, pero es una buena práctica usarlo de todos modos cuando se ordena, en caso de que el método de vapor cambie ...
Steve Chambers
0

En una palabra:

El pedido depende de la estructura de datos de origen y las operaciones de flujo intermedio. Suponiendo que esté utilizando un, Listel procesamiento debe ordenarse (ya filterque no cambiará la secuencia aquí).

Más detalles:

Secuencial vs Paralelo vs No ordenado:

Javadocs

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

Orden de flujo:

Javadocs

Las transmisiones pueden o no tener un orden de encuentro definido. Si una secuencia tiene o no un orden de encuentro depende de la fuente y las operaciones intermedias. Ciertas fuentes de flujo (como List o arrays) están ordenadas intrínsecamente, mientras que otras (como HashSet) no lo están. Algunas operaciones intermedias, como sorted (), pueden imponer un orden de encuentro en una secuencia no ordenada y otras pueden hacer que una secuencia ordenada no esté ordenada, como BaseStream.unordered (). Además, algunas operaciones de terminal pueden ignorar el orden de encuentro, como forEach ().

Si se ordena una secuencia, la mayoría de las operaciones están obligadas a operar en los elementos en su orden de encuentro; si la fuente de una secuencia es una Lista que contiene [1, 2, 3], entonces el resultado de ejecutar el mapa (x -> x * 2) debe ser [2, 4, 6]. Sin embargo, si la fuente no tiene un orden de encuentro definido, cualquier permutación de los valores [2, 4, 6] sería un resultado válido.

Para flujos secuenciales, la presencia o ausencia de un orden de encuentro no afecta el rendimiento, solo el determinismo. Si se ordena una secuencia, la ejecución repetida de canalizaciones de secuencia idénticas en una fuente idéntica producirá un resultado idéntico; Si no se ordena, la ejecución repetida puede producir resultados diferentes.

Para flujos paralelos, relajar la restricción de ordenamiento a veces puede permitir una ejecución más eficiente. Ciertas operaciones agregadas, como el filtrado de duplicados (distintos ()) o reducciones agrupadas (Collectors.groupingBy ()) se pueden implementar de manera más eficiente si el orden de los elementos no es relevante. Del mismo modo, las operaciones que están intrínsecamente vinculadas al orden de encuentro, como limit (), pueden requerir almacenamiento en búfer para garantizar un orden adecuado, lo que socava el beneficio del paralelismo. En los casos en que la secuencia tiene un orden de encuentro, pero el usuario no se preocupa particularmente por ese orden de encuentro, desordenar explícitamente la secuencia con no ordenado () puede mejorar el rendimiento paralelo para algunas operaciones con estado o terminales. Sin embargo, la mayoría de las tuberías de flujo, como el ejemplo de "suma de peso de bloques" anterior,

Saikat
fuente