Guia para o Java 8 forEach
1. Visão geral
Introduzido no Java 8, o loopforEach fornece aos programadoresa new, concise and interesting way for iterating over a collection.
Neste artigo, veremos como usarforEach com coleções, que tipo de argumento é usado e como esse loop difere dofor-loop aprimorado.
Se você precisa retocar alguns conceitos do Java 8, temos umcollection of articles que pode ajudá-lo.
2. Noções básicas deforEach
Em Java, a interfaceCollection temIterable como sua superinterface - e começando com Java 8, essa interface tem uma nova API:
void forEach(Consumer super T> action)
Simplificando, as estatísticas deJavadoc deforEach que“performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.”
E assim, comforEach, podemos iterar sobre uma coleção e executar uma determinada ação em cada elemento, como qualquer outroIterator.
Por exemplo, uma versãofor-loop de iteração e impressão deCollection deStrings:
for (String name : names) {
System.out.println(name);
}
Podemos escrever isso usandoforEach como:
names.forEach(name -> {
System.out.println(name);
});
3. Usando o métodoforEach
UsamosforEach para iterar sobre uma coleção e executar uma determinada ação em cada elemento. The action to be performed is contained in a class that implements the Consumer interface and is passed to forEach as an argument.
A interfaceConsumer éa functional interface (uma interface com um único método abstrato). Ele aceita uma entrada e não retorna nenhum resultado.
Aqui está a definição:
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
Portanto, qualquer implementação, por exemplo, um consumidor que simplesmente imprime umString:
Consumer printConsumer = new Consumer() {
public void accept(String name) {
System.out.println(name);
};
};
pode ser passado paraforEach como um argumento:
names.forEach(printConsumer);
Mas essa não é a única maneira de criar uma ação por meio de um consumidor e usando a APIforEach.
Vejamos as 3 maneiras mais populares de usar o métodoforEach:
3.1. Implementação deConsumer anônima
Podemos instanciar uma implementação da interfaceConsumer usando uma classe anônima e então aplicá-la como um argumento para o métodoforEach:
Consumer printConsumer= new Consumer() {
public void accept(String name) {
System.out.println(name);
}
};
names.forEach(printConsumer);
Isso funciona bem, mas se analisarmos o exemplo acima, veremos que a parte real que é útil é o código dentro do métodoaccept().
Embora as expressões Lambda sejam agora a norma e a maneira mais fácil de fazer isso, ainda vale a pena saber como implementar a interfaceConsumer.
3.2. Uma Expressão Lambda
O principal benefício das interfaces funcionais do Java 8 é que podemos usar expressões Lambda para instancia-las e evitar o uso de implementações de classes anônimas volumosas.
Como a InterfaceConsumer é uma interface funcional, podemos expressá-la em Lambda na forma de:
(argument) -> { //body }
Portanto, nossoprintConsumer simplifica para:
name -> System.out.println(name)
E podemos passá-lo paraforEach as:
names.forEach(name -> System.out.println(name));
Desde a introdução das expressões Lambda no Java 8, esta é provavelmente a maneira mais comum de usar o métodoforEach.
Lambdas tem uma curva de aprendizado muito real, então se você está começando,this write-up aborda algumas boas práticas para trabalhar com o novo recurso de linguagem.
3.3. Uma referência de método
Podemos usar a sintaxe de referência do método em vez da sintaxe normal do Lambda, onde já existe um método para executar uma operação na classe:
names.forEach(System.out::println);
4. Trabalhando comforEach
4.1. Iterando emCollection
Qualquer iterável do tipoCollection – list, set, queue etc. tem a mesma sintaxe para usarforEach.
Portanto, como já vimos, iteramos elementos de uma lista:
List names = Arrays.asList("Larry", "Steve", "James");
names.forEach(System.out::println);
Da mesma forma para um conjunto:
Set uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James"));
uniqueNames.forEach(System.out::println);
Ou digamos que para umQueue que também é umCollection:
Queue namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James"));
namesQueue.forEach(System.out::println);
4.2. Iterando sobre um mapa - usandoforEach do mapa
Mapas não sãoIterable, mas simprovide their own variant of forEach that accepts a*BiConsumer*.
UmBiConsumer foi introduzido em vez deConsumer emforEach do Iterable para que uma ação possa ser realizada na chave e no valor de aMap simultaneamente.
Vamos criar umMap com entradas:
Map namesMap = new HashMap<>();
namesMap.put(1, "Larry");
namesMap.put(2, "Steve");
namesMap.put(3, "James");
A seguir, vamos iterarnamesMap usandoforEach do mapa:
namesMap.forEach((key, value) -> System.out.println(key + " " + value));
Como podemos ver aqui, usamos umBiConsumer:
(key, value) -> System.out.println(key + " " + value)
para iterar nas entradas deMap.
4.3. Iterando emMap – by iterating entrySet
Também podemos iterarEntrySet of aMap using IterableforEach.
Desdethe entries of a Map are stored in a Set called EntrySet, we can iterate that using a forEach:
namesMap.entrySet().forEach(entry -> System.out.println(
entry.getKey() + " " + entry.getValue()));
5. Foreach vs For-Loop
De um ponto de vista simples, os dois loops fornecem a mesma funcionalidade - percorrem os elementos de uma coleção.
The main difference between the two of them is that they are different iterators – the enhanced for-loop is an external iterator whereas the new forEach method is an internal one.
5.1. Iterador interno -forEach
Esse tipo de iterador gerencia a iteração em segundo plano e deixa o programador apenas codificar o que deve ser feito com os elementos da coleção.
O iterador, em vez disso, gerencia a iteração e certifica-se de processar os elementos um por um.
Vejamos um exemplo de um iterador interno:
names.forEach(name -> System.out.println(name));
No métodoforEach acima, podemos ver que o argumento fornecido é uma expressão lambda. Isso significa que o método só precisa conhecerwhat is to be donee todo o trabalho de iteração será feito internamente.
5.2. Iterador externo -for-loop
Os iteradores externos misturam owhate ohow que o loop deve ser feito.
Enumerations,Iteratorse aprimoradofor-loop são todos iteradores externos (lembre-se dos métodositerator(),next() ouhasNext()? ). Em todos esses iteradores, é nosso trabalho especificar como realizar as iterações.
Considere este loop familiar:
for (String name : names) {
System.out.println(name);
}
Embora não estejamos invocando explicitamente os métodoshasNext() ounext() enquanto iteramos na lista, o código subjacente que faz essa iteração funcionar usa esses métodos. Isso implica que a complexidade dessas operações está oculta do programador, mas ainda existe.
Ao contrário de um iterador interno no qual a coleção faz a própria iteração, aqui exigimos código externo que retire todos os elementos da coleção.
6. Conclusão
Neste artigo, mostramos que o loopforEach é mais conveniente do que ofor-loop normal.
Também vimos como o métodoforEach funciona e que tipo de implementação pode receber como argumento para realizar uma ação em cada elemento da coleção.
Finalmente, todos os snippets usados neste artigo estão disponíveis em nosso repositórioGithub.