Considere um List<String> stringListque pode ser impresso de várias maneiras usando construções 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)
Como essas abordagens são diferentes umas das outras?
Primeira abordagem ( Iterable.forEach) -
o iterador da coleção geralmente é usado e é projetado para ser rápido, o que significa que será lançado ConcurrentModificationExceptionse a coleção subjacente for estruturalmente modificada durante a iteração. Conforme mencionado no documento para ArrayList:
Uma modificação estrutural é qualquer operação que adiciona ou exclui um ou mais elementos ou redimensiona explicitamente a matriz de suporte; apenas definir o valor de um elemento não é uma modificação estrutural.
Então, isso significa ArrayList.forEachque a definição do valor é permitida sem nenhum problema. E no caso de coleta simultânea, por exemplo, ConcurrentLinkedQueueo iterador seria fracamente consistente, o que significa que as ações transmitidas forEachpodem fazer alterações estruturais mesmo sem que ConcurrentModificationExceptionsejam lançadas exceções. Mas aqui as modificações podem ou não ser visíveis nessa iteração.
Segunda abordagem ( Stream.forEach) -
o pedido não está definido. Embora possa não ocorrer para fluxos sequenciais, mas a especificação não garante isso. Também é necessário que a ação seja de natureza não interferente. Como mencionado no doc :
O comportamento desta operação é explicitamente não determinístico. Para pipelines de fluxo paralelo, esta operação não garante o respeito à ordem de encontro do fluxo, pois isso sacrificaria o benefício do paralelismo.
Terceira abordagem ( Stream.forEachOrdered) -
a ação seria executada na ordem de encontro do fluxo. Portanto, sempre que a ordem importa, use forEachOrderedsem pensar duas vezes. Conforme mencionado no documento :
Executa uma ação para cada elemento desse fluxo, na ordem de encontro do fluxo, se o fluxo tiver uma ordem de encontro definida.
Ao iterar sobre uma coleção sincronizada, a primeira abordagem retira o bloqueio da coleção uma vez e o mantém em todas as chamadas para o método de ação, mas no caso de fluxos, eles usam o spliterator da coleção, que não bloqueia e depende das regras já estabelecidas de -interferência. Caso a coleta de backup do fluxo seja modificada durante a iteração ConcurrentModificationException, será lançado um resultado inconsistente.
Quarta Abordagem (Paralela Stream.forEach) -
Como já mencionado, não há garantia de respeitar a ordem do encontro como esperado no caso de fluxos paralelos. É possível que a ação seja executada em um encadeamento diferente para diferentes elementos que nunca podem ser o caso forEachOrdered.
Quinto Approach (paralela Stream.forEachOrdered) -
A forEachOrderedvai processar os elementos na ordem especificada pela fonte, independentemente do facto de se transmitir é sequencial ou paralelo. Portanto, não faz sentido usar isso com fluxos paralelos.
Listser? Mostre-nos como você o declarou e instanciaram.