Guia para o Java 8 forEach

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 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.