Guide de Java 8 pour chaque

Guide de Java 8 pour chaque

1. Vue d'ensemble

Introduite dans Java 8, la boucleforEach fournit aux programmeurs desa new, concise and interesting way for iterating over a collection.

Dans cet article, nous verrons comment utiliserforEach avec des collections, quel type d'argument il prend et en quoi cette boucle diffère desfor-loop améliorés.

Si vous avez besoin de rafraîchir certains concepts de Java 8, nous avons uncollection of articles qui peut vous aider.

2. Principes de base deforEach

En Java, l'interfaceCollection aIterable comme super interface - et à partir de Java 8, cette interface a une nouvelle API:

void forEach(Consumer action)

En termes simples, les statistiquesJavadoc deforEach qu'il“performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.”

Et donc, avecforEach, nous pouvons parcourir une collection et effectuer une action donnée sur chaque élément, comme n'importe quel autreIterator.

Par exemple, une versionfor-loop de l'itération et de l'impression d'unCollection deStrings:

for (String name : names) {
    System.out.println(name);
}

Nous pouvons écrire ceci en utilisantforEach comme:

names.forEach(name -> {
    System.out.println(name);
});

3. Utilisation de la méthodeforEach

Nous utilisonsforEach pour parcourir une collection et effectuer une certaine action sur chaque élément. The action to be performed is contained in a class that implements the Consumer interface and is passed to forEach as an argument.

L'interfaceConsumer esta functional interface (une interface avec une seule méthode abstraite). Il accepte une entrée et ne renvoie aucun résultat.

Voici la définition:

@FunctionalInterface
public interface Consumer {
    void accept(T t);
}

Par conséquent, toute implémentation, par exemple, un consommateur qui imprime simplement unString:

Consumer printConsumer = new Consumer() {
    public void accept(String name) {
        System.out.println(name);
    };
};

peut être passé àforEach comme argument:

names.forEach(printConsumer);

Mais ce n'est pas la seule façon de créer une action via un consommateur et d'utiliser l'API deforEach.

Voyons les 3 façons les plus courantes d'utiliser la méthodeforEach:

3.1. Implémentation anonyme deConsumer

Nous pouvons instancier une implémentation de l'interfaceConsumer en utilisant une classe anonyme, puis l'appliquer comme argument à la méthodeforEach:

Consumer printConsumer= new Consumer() {
    public void accept(String name) {
        System.out.println(name);
    }
};
names.forEach(printConsumer);

Cela fonctionne bien mais si nous analysons l'exemple ci-dessus, nous verrons que la partie réelle qui est utilisée est le code à l'intérieur de la méthodeaccept().

Bien que les expressions Lambda soient désormais la norme et le moyen le plus simple de le faire, il est toujours utile de savoir comment implémenter l'interfaceConsumer.

3.2. Une expression lambda

Le principal avantage des interfaces fonctionnelles de Java 8 est que nous pouvons utiliser des expressions Lambda pour les instancier et éviter d’utiliser de lourdes implémentations de classes anonymes.

Comme l'interfaceConsumer est une interface fonctionnelle, nous pouvons l'exprimer dans Lambda sous la forme de:

(argument) -> { //body }

Par conséquent, nosprintConsumer se simplifient en:

name -> System.out.println(name)

Et nous pouvons le transmettre àforEach as:

names.forEach(name -> System.out.println(name));

Depuis l'introduction des expressions Lambda dans Java 8, c'est probablement la manière la plus courante d'utiliser la méthodeforEach.

Les lambdas ont une courbe d'apprentissage très réelle, donc si vous commencez,this write-up passe en revue quelques bonnes pratiques d'utilisation de la nouvelle fonctionnalité linguistique.

3.3. Une référence de méthode

Nous pouvons utiliser la syntaxe de référence de méthode à la place de la syntaxe Lambda normale dans laquelle une méthode existe déjà pour effectuer une opération sur la classe:

names.forEach(System.out::println);

4. Travailler avecforEach

4.1. Itération sur unCollection

Tout itérable de typeCollection – list, set, queue etc. ont la même syntaxe pour utiliserforEach.

Par conséquent, comme nous l’avons déjà vu, pour parcourir les éléments d’une liste:

List names = Arrays.asList("Larry", "Steve", "James");

names.forEach(System.out::println);

De même pour un ensemble:

Set uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James"));

uniqueNames.forEach(System.out::println);

Ou disons pour unQueue qui est aussi unCollection:

Queue namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James"));

namesQueue.forEach(System.out::println);

4.2. Itérer sur une carte - à l’aide desforEach de la carte

Les cartes ne sont pasIterable, mais elles fontprovide their own variant of forEach that accepts a*BiConsumer*. 

UnBiConsumer a été introduit à la place deConsumer dans lesforEach d'Iterable afin qu'une action puisse être effectuée simultanément sur la clé et la valeur d'unMap.

Créons unMap avec des entrées:

Map namesMap = new HashMap<>();
namesMap.put(1, "Larry");
namesMap.put(2, "Steve");
namesMap.put(3, "James");

Ensuite, parcouronsnamesMap à l’aide desforEach de la carte:

namesMap.forEach((key, value) -> System.out.println(key + " " + value));

Comme nous pouvons le voir ici, nous avons utilisé unBiConsumer:

(key, value) -> System.out.println(key + " " + value)

pour parcourir les entrées desMap.

4.3. Itération sur unMap – by iterating entrySet

Nous pouvons également itérer lesEntrySet of aMap ous lesforEach. d'Iterable

Depuisthe 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

D'un point de vue simple, les deux boucles offrent les mêmes fonctionnalités, à savoir les éléments d'une collection.

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. Itérateur interne -forEach

Ce type d'itérateur gère l'itération en arrière-plan et laisse le programmeur se contenter de coder ce qui doit être fait avec les éléments de la collection.

Au lieu de cela, l’itérateur gère l’itération et s’assure de traiter les éléments un par un.

Voyons un exemple d’itérateur interne:

names.forEach(name -> System.out.println(name));

Dans la méthodeforEach ci-dessus, nous pouvons voir que l'argument fourni est une expression lambda. Cela signifie que la méthode n'a besoin de connaître quewhat is to be done et que tout le travail d'itération sera pris en charge en interne.

5.2. Itérateur externe -for-loop

Les itérateurs externes mélangent leswhat et leshow la boucle doit être effectuée.

Enumerations,Iterators et lesfor-loop améliorés sont tous des itérateurs externes (rappelez-vous les méthodesiterator(),next() ouhasNext()? ). Dans tous ces itérateurs, c'est notre travail de spécifier comment effectuer les itérations.

Considérez cette boucle familière:

for (String name : names) {
    System.out.println(name);
}

Bien que nous n'invoquions pas explicitement les méthodeshasNext() ounext() lors de l'itération sur la liste, le code sous-jacent qui fait fonctionner cette itération utilise ces méthodes. Cela implique que la complexité de ces opérations est cachée au programmeur mais qu'elle existe toujours.

Contrairement à un itérateur interne dans lequel la collection effectue l'itération elle-même, nous avons besoin ici d'un code externe qui extrait chaque élément de la collection.

6. Conclusion

Dans cet article, nous avons montré que la boucleforEach est plus pratique que la bouclefor-loop normale.

Nous avons également vu comment fonctionne la méthodeforEach et quel type d'implémentation peut recevoir en argument pour effectuer une action sur chaque élément de la collection.

Enfin, tous les extraits de code utilisés dans cet article sont disponibles dans notre référentielGithub.