Em Java Streams é recomendado evitar usar estado compartilhado em foreach.
Prova:
java streams doc
Efeitos colaterais Efeitos colaterais em parâmetros comportamentais para operações de fluxo são, em geral, desencorajados, pois podem frequentemente levar a violações involuntárias do requisito de ausência de estado, bem como outros riscos de segurança de thread. Se os parâmetros comportamentais tiverem efeitos colaterais, a menos que explicitamente declarado, não há garantias quanto à visibilidade desses efeitos colaterais para outros threads, nem há garantias de que operações diferentes no "mesmo" elemento dentro do mesmo pipeline de fluxo sejam executadas no mesmo thread. Além disso, a ordenação desses efeitos pode ser surpreendente. Mesmo quando um pipeline é restrito a produzir um resultado que seja consistente com a ordem de encontro da fonte de fluxo (por exemplo, IntStream.range(0,5).parallel().map(x -> x*2).toArray() deve produzir [0, 2, 4, 6, 8]), nenhuma garantia é feita quanto à ordem em que a função mapeadora é aplicada a elementos individuais, ou em qual thread qualquer parâmetro comportamental é executado para um dado elemento.
Muitas computações onde alguém pode ser tentado a usar efeitos colaterais podem ser expressas de forma mais segura e eficiente sem efeitos colaterais, como usar redução em vez de acumuladores mutáveis. No entanto, efeitos colaterais como usar println() para propósitos de depuração são geralmente inofensivos. Um pequeno número de operações de fluxo, como forEach() e peek(), podem operar somente por meio de efeitos colaterais; estes devem ser usados com cuidado.
Como exemplo de como transformar um pipeline de fluxo que usa efeitos colaterais de forma inadequada em um que não o faz, o código a seguir pesquisa um fluxo de strings em busca daquelas que correspondem a uma determinada expressão regular e coloca as correspondências em uma lista.
ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches())
.forEach(s -> results.add(s)); // Unnecessary use of side-effects!
Este código usa efeitos colaterais desnecessariamente. Se executado em paralelo, a não segurança de thread de ArrayList causaria resultados incorretos, e adicionar a sincronização necessária causaria contenção, minando o benefício do paralelismo. Além disso, usar efeitos colaterais aqui é completamente desnecessário; o forEach() pode ser simplesmente substituído por uma operação de redução que é mais segura, mais eficiente e mais passível de paralelismo:
List<String>results =
stream.filter(s -> pattern.matcher(s).matches())
.collect(Collectors.toList()); // No side-effects!
Não consigo encontrar as mesmas recomendações para Kotlin.
Devo evitar esse código em Kotlin?
var bar = foo(myPath)
myPath.forEach { e ->
...
bar = foo(bar,....)
}
Ao contrário dos fluxos do Java que podem ser executados em paralelo, as funções no
kotlin.collections
pacote (forEach
é um deles) nunca são executadas simultaneamente. Portanto, os problemas de segurança de thread mencionados nos javadocs citados não são relevantes.forEach
(entre muitas outras funções emkotlin.collections
) é umainline
função . Isso significa que seu corpo de função é inserido diretamente no site de chamada durante a compilação. O lambda que você passou para ele também é embutido.Ao inspecionar a fonte , podemos ver que a chamada
forEach
é exatamente a mesma que umfor
loop.está alinhado com
Então, no binário compilado, não há mais uma chamada para
forEach
, e não há mais um lamnbda. O código se comporta exatamente da mesma forma como se você tivesse escrito ofor
loop em vez de chamarforEach
.