Como garantir a ordem de processamento nos fluxos java8?

148

Eu quero processar listas dentro de um XMLobjeto java. Eu tenho que garantir o processamento de todos os elementos para que eu os receba.

Devo, portanto, chamar sequentialcada um que streameu uso? list.stream().sequential().filter().forEach()

Ou é suficiente apenas usar o fluxo, desde que eu não use paralelismo? list.stream().filter().forEach()

som do membro
fonte

Respostas:

338

Você está fazendo a pergunta errada. Você está perguntando sobre sequentialvs., parallelenquanto deseja processar os itens em ordem , portanto, é necessário perguntar sobre pedidos . Se você possui um fluxo ordenado e realiza operações que garantem manter o pedido, não importa se o fluxo é processado em paralelo ou sequencialmente; a implementação manterá a ordem.

A propriedade ordenada é distinta de paralela e seqüencial. Por exemplo, se você chamar stream()um HashSetfluxo, não será ordenado ao chamar stream()um Listretorno de um fluxo ordenado. Observe que você pode ligar unordered()para liberar o contrato de pedido e potencialmente aumentar o desempenho. Uma vez que o fluxo não tem pedidos, não há como restabelecer o pedido. (A única maneira de transformar um fluxo não ordenado em um ordenado é chamar sorted, no entanto, o pedido resultante não é necessariamente o pedido original).

Consulte também a seção "Pedidos" da java.util.streamdocumentação do pacote .

Para garantir a manutenção do pedido em toda uma operação de fluxo, você deve estudar a documentação da fonte do fluxo, todas as operações intermediárias e a operação do terminal para manter ou não o pedido (ou se a fonte tem um pedido no primeiro Lugar, colocar).

Isso pode ser muito sutil, por exemplo, Stream.iterate(T,UnaryOperator)cria um fluxo ordenado enquanto Stream.generate(Supplier)cria um fluxo não ordenado . Observe que você também cometeu um erro comum em sua pergunta, pois não mantém a ordem. Você precisa usar se quiser processar os elementos do fluxo em uma ordem garantida.forEach forEachOrdered

Portanto, se a listsua pergunta for de fato a java.util.List, o stream()método retornará um fluxo ordenado e filternão alterará a ordem. Portanto, se você chamar list.stream().filter() .forEachOrdered(), todos os elementos serão processados ​​sequencialmente em ordem, enquanto list.parallelStream().filter().forEachOrdered()os elementos podem ser processados ​​em paralelo (por exemplo, pelo filtro), mas a ação do terminal ainda será chamada em ordem (o que obviamente reduzirá o benefício da execução paralela) .

Se você, por exemplo, usar uma operação como

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

a operação inteira pode se beneficiar da execução paralela, mas a lista resultante sempre estará na ordem correta, independentemente de você usar um fluxo paralelo ou sequencial.

Holger
fonte
48
Sim, boa resposta. Uma coisa que descobri é que a terminologia que usamos, pelo menos em inglês, como "antes", "depois" e assim por diante, é bastante ambígua. Existem dois tipos de pedidos aqui: 1) ordem de encontro (também conhecida como ordem espacial ) e 2) ordem de processamento (também conhecida como ordem temporal ). Com essa distinção em mente, pode ser útil usar palavras como "esquerda de" ou "direito de" ao discutir ordem de encontro e "anterior a" ou "posterior a" ao discutir ordem de processamento.
Stuart Marks
Eu entendo List<>preservará a ordem, mas será Collection<>?
22418 Josh C.
5
@JoshC. isso depende do tipo de coleção real. Sets geralmente não, a menos que seja um SortedSetou LinkedHashSet. Os pontos de vista de cobrança de um Map( keySet(), entrySet()e values()) herdar a Mappolítica de 's, ou seja, são ordenados quando o mapa é um SortedMapou LinkedHashMap. O comportamento é determinado pelas características relatadas pelo separador da coleção . A defaultimplementação de Collectionnão relata a ORDEREDcaracterística, portanto não é ordenada, a menos que seja substituída.
Holger
@ Holger Eu tive uma pergunta que pode estar relacionada um pouco a uma pequena seção da sua resposta.
Naman 23/01
1
Vale a pena notar que isso forEachOrdereddifere apenas ao forEachusar fluxos paralelos - mas é uma boa prática usá-lo de qualquer maneira ao fazer pedidos, caso o método de vapor sempre mude ...
Steve Chambers
0

Em poucas palavras:

A ordem depende da estrutura de dados de origem e das operações de fluxo intermediário. Supondo que você esteja usando um, Listo processamento deve ser solicitado (já filterque não mudará a sequência aqui).

Mais detalhes:

Sequencial vs Paralelo vs Não 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.

Ordenação de stream:

Javadocs

Os fluxos podem ou não ter uma ordem de encontro definida. O fato de um fluxo ter ou não uma ordem de encontro depende da origem e das operações intermediárias. Certas fontes de fluxo (como Lista ou matrizes) são ordenadas intrinsecamente, enquanto outras (como HashSet) não são. Algumas operações intermediárias, como Sorted (), podem impor uma ordem de encontro em um fluxo não ordenado e outras podem tornar um fluxo ordenado não ordenado, como BaseStream.unordered (). Além disso, algumas operações do terminal podem ignorar a ordem de encontro, como forEach ().

Se um fluxo é ordenado, a maioria das operações é restrita a operar nos elementos em sua ordem de encontro; se a origem de um fluxo for uma Lista contendo [1, 2, 3], o resultado da execução do mapa (x -> x * 2) deverá ser [2, 4, 6]. No entanto, se a fonte não tiver uma ordem de encontro definida, qualquer permutação dos valores [2, 4, 6] seria um resultado válido.

Para fluxos sequenciais, a presença ou ausência de uma ordem de encontro não afeta o desempenho, apenas o determinismo. Se um fluxo for ordenado, a execução repetida de pipelines de fluxo idênticos em uma fonte idêntica produzirá um resultado idêntico; se não for ordenada, a execução repetida poderá produzir resultados diferentes.

Para fluxos paralelos, relaxar a restrição de pedidos às vezes pode permitir uma execução mais eficiente. Certas operações agregadas, como filtragem de duplicatas (distintas ()) ou reduções agrupadas (Collectors.groupingBy ()), podem ser implementadas com mais eficiência se a ordem dos elementos não for relevante. Da mesma forma, operações intrinsecamente vinculadas para encontrar ordem, como limit (), podem exigir buffer para garantir a ordem correta, prejudicando o benefício do paralelismo. Nos casos em que o fluxo tem uma ordem de encontro, mas o usuário não se importa particularmente com essa ordem de encontro, a ordenação explícita do fluxo com desordenado () pode melhorar o desempenho paralelo de algumas operações com estado ou terminal. No entanto, a maioria dos pipelines de fluxo, como o exemplo da "soma do peso dos blocos" acima,

Saikat
fonte