CodeGym /Cours /JAVA 25 SELF /Utilisation des lambdas dans les collections et les strea...

Utilisation des lambdas dans les collections et les streams

JAVA 25 SELF
Niveau 48 , Leçon 1
Disponible

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
forEach(Consumer<T>)
Pour chaque élément
x -> ...
list.forEach(s -> ...)
removeIf(Predicate<T>)
Supprime des éléments selon une condition
x -> ...
list.removeIf(s -> ...)
sort(Comparator<T>)
Trie les éléments
(a, b) -> ...
list.sort((a, b) -> ...)
replaceAll(UnaryOperator<T>)
Remplace chaque élément
x -> ...
list.replaceAll(s -> ...)
filter(Predicate<T>)
Filtre le stream
x -> ...
stream.filter(s -> ...)
map(Function<T, R>)
Transforme les éléments
x -> ...
stream.map(s -> ...)
forEach(Consumer<T>)
Itération sur le stream
x -> ...
stream.forEach(s -> ...)
sorted(Comparator<T>)
Tri dans le stream
(a, b) -> ...
stream.sorted((a, b) -> ...)

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.

1
Mission
JAVA 25 SELF, niveau 48, leçon 1
Bloqué
Système "PRODUITS CRIANTS" 📢
Système "PRODUITS CRIANTS" 📢
1
Mission
JAVA 25 SELF, niveau 48, leçon 1
Bloqué
Expédition "Grandes Eaux" 🗺️
Expédition "Grandes Eaux" 🗺️
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION