1. Expressions lambda et collections
Rappelons comment on traitait les collections avant l’apparition des expressions lambda. Supposons que nous ayons une liste de chaînes et que nous voulions les afficher à l’écran :
List<String> list = Arrays.asList("chat", "chien", "hérisson");
for (String s : list) {
System.out.println(s);
}
Tout est simple, mais si nous voulons, par exemple, supprimer de la liste toutes les chaînes vides, il faut écrire une boucle avec une condition, et parfois utiliser un itérateur (sinon, il y aura une ConcurrentModificationException). Ou, disons, trier par longueur de chaîne :
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
Même pour une tâche aussi simple — déjà 5 lignes de code et une tonne d’accolades « bruitées ». Comment optimiser ? Vous connaissez déjà la réponse : les expressions lambda.
Utilisation des expressions lambda dans les méthodes des collections
Depuis Java 8, les interfaces de collections ont reçu de nouvelles méthodes qui acceptent des interfaces fonctionnelles — ce qui signifie que nous pouvons y passer des expressions lambda. Voici les plus populaires :
- forEach(Consumer<T> action)
- removeIf(Predicate<T> filter)
- sort(Comparator<T> c)
- replaceAll(UnaryOperator<T> operator)
Exemple : forEach
Afficher tous les éléments de la liste (ancienne méthode) :
for (String s : list) {
System.out.println(s);
}
Maintenant — avec une lambda :
list.forEach(s -> System.out.println(s));
Ou encore plus court, si vous voulez jouer au « gourou Java » :
list.forEach(System.out::println); // reference de méthode, nous l’aborderons plus tard
Exemple : removeIf
Supprimer toutes les chaînes vides de la liste :
List<String> animals = new ArrayList<>(Arrays.asList("chat", "", "chien", "hérisson", ""));
animals.removeIf(s -> s.isEmpty());
System.out.println(animals); // [chat, chien, hérisson]
Exemple : sort
Trier la liste par longueur de chaîne :
List<String> animals = Arrays.asList("chat", "chien", "hérisson", "éléphant");
animals.sort((a, b) -> a.length() - b.length());
System.out.println(animals); // [chat, chien, hérisson, éléphant]
Exemple : replaceAll
Convertir toutes les chaînes en majuscules :
List<String> animals = new ArrayList<>(Arrays.asList("chat", "chien", "hérisson"));
animals.replaceAll(s -> s.toUpperCase());
System.out.println(animals); // [CHAT, CHIEN, HÉRISSON]
2. Stream API et expressions lambda
Avec Java 8 est apparue la Stream API — un outil puissant pour traiter les collections dans un style fonctionnel. Les streams permettent de filtrer, transformer, trier, collecter des collections à l’aide de chaînes de méthodes. Et toutes ces méthodes acceptent des expressions lambda !
Important : Une analyse complète de la Stream API viendra plus tard, pour l’instant — seulement des exemples de base pour comprendre le rôle des lambdas.
Exemple : filtrage
Conserver uniquement les chaînes plus longues que 3 caractères :
List<String> animals = Arrays.asList("chat", "éléphant", "hérisson", "crocodile");
animals.stream()
.filter(s -> s.length() > 3)
.forEach(System.out::println); // chat, éléphant, hérisson, crocodile
Exemple : transformation (map)
Mettre toutes les chaînes en majuscules :
List<String> animals = Arrays.asList("chat", "éléphant", "hérisson");
List<String> upper = animals.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
System.out.println(upper); // [CHAT, ÉLÉPHANT, HÉRISSON]
Exemple : tri
Obtenir une liste triée par longueur (sans modifier l’originale) :
List<String> animals = Arrays.asList("chat", "éléphant", "hérisson", "crocodile");
List<String> sorted = animals.stream()
.sorted((a, b) -> a.length() - b.length())
.collect(Collectors.toList());
System.out.println(sorted); // [chat, éléphant, hérisson, crocodile]
3. Comparaison avec les classes anonymes
Comparons à quoi ressemblerait le même code avec une classe anonyme et avec une lambda.
Tri par longueur de chaîne
Classe anonyme :
animals.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
Expression lambda :
animals.sort((a, b) -> a.length() - b.length());
Conclusion :
Une expression lambda économise beaucoup de lignes et rend le code plus lisible. Moins d’accolades, moins de bruit — plus d’essentiel !
Suppression des chaînes vides
Classe anonyme :
animals.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
});
Expression lambda :
animals.removeIf(s -> s.isEmpty());
4. Pratique : petites tâches avec des expressions lambda
Essayons en pratique d’appliquer les expressions lambda dans un mini-programme de gestion d’une liste d’utilisateurs.
Exemple 1 : filtrer des utilisateurs par âge
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
List<User> users = Arrays.asList(
new User("Alice", 17),
new User("Bob", 25),
new User("Charlie", 15)
);
users.stream()
.filter(u -> u.age >= 18)
.forEach(System.out::println); // Bob (25)
Exemple 2 : trier les utilisateurs par nom
List<User> users = Arrays.asList(
new User("Alice", 17),
new User("Bob", 25),
new User("Charlie", 15)
);
users.sort((u1, u2) -> u1.name.compareTo(u2.name));
System.out.println(users);
// [Alice (17), Bob (25), Charlie (15)]
Exemple 3 : transformer la liste des utilisateurs en liste de noms
List<String> names = users.stream()
.map(u -> u.name)
.collect(Collectors.toList());
System.out.println(names); // [Alice, Bob, Charlie]
Exemple 4 : supprimer tous les mineurs
List<User> users = new ArrayList<>(Arrays.asList(
new User("Alice", 17),
new User("Bob", 25),
new User("Charlie", 15)
));
users.removeIf(u -> u.age < 18);
System.out.println(users); // [Bob (25)]
5. Détails utiles
Particularités : portée et variables « effectivement final »
Une expression lambda peut utiliser des variables de la méthode englobante, mais seulement si elles sont final ou « effectivement final » (c’est-à-dire non modifiées après l’initialisation).
int minAge = 18;
users.stream()
.filter(u -> u.age >= minAge)
.forEach(System.out::println);
Si vous essayez de modifier minAge après son utilisation dans la lambda — le compilateur générera une erreur.
Tableau : principales méthodes des collections et des streams avec des expressions lambda
| Méthode de collection/stream | Ce que ça fait | Type d’expression lambda | Exemple |
|---|---|---|---|
|
Pour chaque élément | |
|
|
Supprime des éléments selon une condition | |
|
|
Trie les éléments | |
|
|
Remplace chaque élément | |
|
|
Filtre le stream | |
|
|
Transforme les éléments | |
|
|
Itération sur le stream | |
|
|
Tri dans le stream | |
|
7. Erreurs courantes
Erreur n°1 : une lambda trop longue. Si, à l’intérieur d’une expression lambda, vous avez déjà 5 lignes de code, des conditions, des boucles et des try-catch — il vaut probablement mieux extraire ce code dans une méthode séparée. Les lambdas sont adaptées à une logique courte.
Erreur n°2 : utilisation de variables mutables. Si vous essayez, dans une lambda, de modifier une variable de la méthode englobante (par exemple, un compteur), le compilateur ne vous le permettra pas. La variable doit être final ou effectivement final.
Erreur n°3 : oublier que les méthodes des collections/streams ne modifient pas toujours la collection d’origine. Par exemple, stream().filter(...) ne modifie pas la liste d’origine, mais renvoie un nouveau stream. Si vous voulez obtenir une collection — utilisez collect(Collectors.toList()).
Erreur n°4 : l’expression lambda ne correspond pas au type attendu. Si une méthode accepte, par exemple, un Comparator<T>, et que vous essayez de passer une lambda avec un seul paramètre (et non deux) — il y aura une erreur de compilation.
Erreur n°5 : perte de lisibilité avec des lambdas imbriquées. Si vous avez une chaîne de map, filter, forEach, et qu’à l’intérieur de chacune se trouve encore une lambda — le code devient illisible. Dans ces cas, il vaut mieux découper les expressions en étapes séparées ou extraire des parties dans des méthodes.
GO TO FULL VERSION