Lambdas et streams
Cours · programmation Java (école d'ingénieur), chapitre 13 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Au chapitre 12, trier une liste d'étudiants par nom nous a demandé d'écrire une classe entière, ParNom, pour une seule ligne utile : return a.nom.compareTo(b.nom);. Et pour calculer la moyenne des notes au-dessus de 10, il fallait une boucle, un test, deux variables d'accumulation. Depuis Java 8, on peut écrire ces deux traitements en une ligne chacun :
promo.sort(Comparator.comparing(Etudiant::getNom));
double m = notes.stream().filter(n -> n >= 10).mapToDouble(n -> n).average().orElse(0);
Ce chapitre présente les deux outils qui le permettent. Les lambdas sont des fonctions sans nom, que l'on passe en argument comme n'importe quelle valeur. Les streams enchaînent des traitements sur une suite de données : filtrer, transformer, trier, regrouper, résumer.
Objectifs du chapitre
- Passer d'une classe anonyme à une lambda, puis à une référence de méthode.
- Connaître les interfaces fonctionnelles les plus usuelles :
Predicate,Function,Consumer,Supplier,BiFunctionetUnaryOperator. - Écrire des comparateurs composés avec
Comparator.comparing. - Utiliser les méthodes des collections qui prennent une lambda (
forEach,removeIf,replaceAll,merge,computeIfAbsent). - Construire un pipeline de stream : source, opérations intermédiaires, opération terminale ; utiliser
CollectorsetOptional. - Comprendre l'évaluation paresseuse et éviter les pièges classiques.
13.1 De la classe anonyme à la lambda
13.1.1 Une classe anonyme
Pour passer un comparateur à Collections.sort, il faut un objet qui réalise l'interface Comparator. Plutôt que de nommer une classe pour un seul usage, Java permet de la définir et de l'instancier sur place : c'est une classe anonyme.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class AnonymeVersLambda {
public static void main(String[] args) {
List<String> mots = new ArrayList<>(List.of("kiwi", "ananas", "figue", "pomme", "noix"));
// 1. Classe anonyme : on définit et on crée l'objet comparateur sur place.
Collections.sort(mots, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
});
System.out.println(mots);
// 2. Lambda : seule la partie utile reste.
mots.sort((a, b) -> Integer.compare(b.length(), a.length()));
System.out.println(mots);
}
}
[kiwi, noix, figue, pomme, ananas]
[ananas, figue, pomme, kiwi, noix]
Le premier tri range les mots par longueur croissante ; à longueur égale, ils gardent leur ordre de départ (le tri est stable). Le second, par longueur décroissante.
13.1.2 La lambda
Dans la classe anonyme, presque tout est imposé : le nom de l'interface, le nom de la méthode, les types des paramètres. Seuls comptent les noms des paramètres et le corps. La lambda ne garde que cela : (a, b) -> Integer.compare(b.length(), a.length()).
Une lambda est une fonction anonyme, écrite (paramètres) -> corps. Le corps est soit une expression, dont la valeur est renvoyée, soit un bloc { ... } contenant des instructions et un return. Une lambda est une valeur : on peut la ranger dans une variable, la passer en argument, la renvoyer.
| Forme | Exemple |
|---|---|
| un paramètre, sans parenthèses | x -> x x |
| plusieurs paramètres | (a, b) -> a + b |
| aucun paramètre | () -> "bonjour" |
| types explicites (rarement utile) | (String s, int n) -> s.length() > n |
| corps en bloc | x -> { int y = 2 x; return y + 1; } |
13.1.3 Interfaces fonctionnelles
Une lambda n'a pas de type par elle-même : elle prend celui de la variable ou du paramètre qui la reçoit, qui doit être une interface fonctionnelle.
Une interface fonctionnelle est une interface qui n'a qu'une seule méthode abstraite. Une lambda en fournit l'implémentation. L'annotation @FunctionalInterface demande au compilateur de le vérifier.
Comparator<T> en est une (sa méthode compare). Le paquet java.util.function fournit les plus courantes :
| Interface | Méthode | Rôle |
|---|---|---|
Predicate | boolean test(T x) | une condition sur x |
Function | R apply(T x) | transforme un T en R |
Consumer | void accept(T x) | fait quelque chose de x |
Supplier | T get() | fournit une valeur |
BiFunction | R apply(T x, U y) | fonction de deux arguments |
UnaryOperator | T apply(T x) | fonction d'un T vers un T |
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.