O forEach() é uma função Java introduzida no Java 8, como parte da API de Streams e da programação funcional. Ele está presente na interface Iterable. Sendo, portanto, comumente utilizado em coleções do tipo List, Set, Map, Arrays, etc.
Sua finalidade é executar uma ação específica para cada elemento da coleção (ou sequência de elementos) sem a necessidade de usar explicitamente um Loop.
Segundo o Javadoc, ele “realiza uma ação em cada elemento, em ordem, até que todos sejam processados, ou até que uma exceção seja lançada”.
Sintaxe
arrayOuCollection.forEach(elemento -> {
// Ação
})
Para ficar mais claro, ele pode ser lido da desta forma na linguagem natural:
“Para cada elemento dentro no arrayOuCollection, execute a seguinte ação”.
Note que, em um for-loop (enhanced) tradicional, ele seria escrito da seguinte forma:
for (Tipo elemento : arrayOuCollection) {
// Ação
}
E também pode ser representado graficamente conforme diagrama abaixo:
Aplicações
A forma de utilização do forEach() depende do contexto. Trago aqui algumas das variações de utilização e suas respectivas sintaxes.
→ Lista:
List<String> lista = Arrays.asList("A", "B", "C");
▸ Lamba Expression:
lista.forEach(elemento -> System.out.println(elemento));
▸ Referência de método:
lista.forEach(System.out::println);
▸ Streams:
lista.stream().forEach(elemento -> System.out.println(elemento));
Saída:
A
B
C
▸ Filter:
lista.stream().filter( element -> element == "B").forEach(System.out::println);
Saída:
B
→ Set:
Set<String> letras = new HashSet<>(Arrays.asList("A", "B", "C"));
letras.forEach(System.out::println);
Saída:
A
B
C
→ Map:
Map<Integer, String> mapa = new HashMap<>();
mapa.put(1, "A");
mapa.put(2, "B");
mapa.put(3, "C");
mapa.forEach((chave, valor) -> System.out.println(chave + ": " + valor));
Saída:
1: A
2: B
3: C
Quando Utilizar o forEach()?
A partir do que vimos até agora, podemos inferir algumas características do método que são importantes salientar, e fazem a diferença na definição do uso do mesmo em um código. Tendo isso em mente, vejamos quando podemos ou devemos utilizar o forEach():
→ Interações simples. Ele substitui o loop tradicional, funcionando, no entanto, da mesma forma;
→ Expressões Lambda ou Referência de Método, que deixam o código mais conciso;
→ Operações em Collections;
→ Operações em paralelo, trabalhando com Streams;
→ Para evitar erros com índices, e quando não há necessidade do uso destes.
Quando não Utilizar o forEach()?
Embora ele seja uma versão simplificada em relação ao For tradicional, existem algumas situações em que ele não pode ser utilizado, e a maioria delas, consequência da não utilização de índices em sua sintaxe. São elas:
→ Adicionar, modificar ou remover um elemento;
→ Recuperar um conjunto de valores;
→ Acessar um índice de um elemento;
→ Quando há necessidade de mais controle sobre a iteração;
→ Lógicas mais complexas, com diversas condições e múltiplas coleções em paralelo.
Para estas situações, a melhor opção é utilizar o For loop tradicional.
Vantagens x Desvantagens
Com as características trazidas até o momento, já podemos ter uma ideia de quais são as vantagens e desvantagens de usar o forEach(). Vejamos quais são:
Vantagens
1 – Legibilidade;
2 – Evita erros com índices;
3 – A própria função se encarrega de fazer o loop sobre os elementos.
4 – É de fácil adoção;
5 – Permite o uso de expressões lambas e referências de métodos;
6 – Pode ser usado com Streams para realizar operações em paralelo em coleções grandes.
Desvantagens
1 – É menos flexível do que o For tradicional, já que não pode ser usado para iterar em algumas situações;
2 – Não fornece acesso ao índice ou a uma variável contadora, o que pode ser útil em diversas situações;
3 – Falta de controle sobre as iterações;
4 – Não permite a propagação de exceções checadas (checked exceptions). Se uma exceção for lançada, ela deve ser tratada dentro do bloco forEach, não podendo ser propagadas pelo método chamador
Conclusão
No geral, o forEach() simplifica a iteração em Java, tornando o código mais conciso e legível, e elimina a necessidade de lidar explicitamente com índices. Isso reduz a probabilidade de erros, mas também impossibilita o uso do mesmo em algumas situações.
Sendo assim, é crucial examinar em quais circunstâncias ele pode ser empregado e se adequará de maneira mais eficaz.
Mais informações podem ser encontradas em:
Javadoc – forEach
Baeldung
Javadoc – Iterable