Adloun

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, BiFunction et UnaryOperator.
  • É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 Collectors et Optional.
  • 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()).

Définition 13.1Lambda

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.

FormeExemple
un paramètre, sans parenthèsesx -> 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 blocx -> { 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.

Définition 13.2Interface 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 :

InterfaceMéthodeRôle
Predicateboolean test(T x)une condition sur x
FunctionR apply(T x)transforme un T en R
Consumervoid accept(T x)fait quelque chose de x
SupplierT get()fournit une valeur
BiFunctionR apply(T x, U y)fonction de deux arguments
UnaryOperatorT 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 cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →