HOWTO · Java

L'opération de réduction de Stream en Java

Cet article présente l'opération de réduction de Stream en Java.

Sur cette page

reduce() combine les éléments d’un flux pour produire un résultat, par exemple une somme ou une valeur extrême. C’est une opération terminale : une fois exécutée, le flux ne peut plus servir à une autre opération. Contrairement à collect(), qui construit un conteneur de résultats modifiable, la surcharge de reduce() à deux arguments renvoie la valeur produite par sa fonction de réduction.

Ce que fait une réduction dans un flux Java

Une réduction combine des valeurs à plusieurs reprises jusqu’à obtenir un seul résultat. Pour un flux d’entiers, ce résultat peut être leur somme, leur plus petite valeur ou leur plus grande valeur. Une réduction ne renvoie pas un flux unique. Java propose aussi des opérations terminales spécialisées, telles que sum(), min() et max(), pour les tâches numériques courantes.

Fonctionnement de reduce() à deux arguments

La surcharge à deux arguments prend la forme reduce(identity, accumulator). L’identité est la valeur de départ du résultat et, lorsqu’elle est transmise à l’accumulateur, elle doit laisser toute valeur inchangée. L’accumulateur prend le résultat partiel actuel et l’élément suivant du flux, puis renvoie un résultat mis à jour. Si une identité est fournie, la réduction d’un flux vide renvoie cette identité.

L’accumulateur doit être sans état, non interférent et associatif : regrouper les mêmes valeurs de différentes façons doit produire le même résultat. L’associativité est particulièrement importante pour les flux parallèles, où Java peut combiner les résultats partiels dans un ordre différent. Par exemple, un accumulateur fondé sur la soustraction n’est pas associatif et ne convient pas à une réduction parallèle. Le type du résultat et les types de l’accumulateur doivent également correspondre à la surcharge utilisée.

Calculer la somme d’un flux avec reduce()

Exécutez cet exemple :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] numbers = {45, 56, 87, 323, 47, 20, 658, 985, 78, 123};
        int sum = Arrays.stream(numbers).reduce(0, (subtotal, number) -> subtotal + number);
        System.out.println("sum: " + sum);
    }
}

Ici, 0 est l’identité et la lambda ajoute chaque nombre au sous-total en cours. Vous pouvez remplacer (subtotal, number) -> subtotal + number par la référence de méthode Integer::sum ; elle exprime la même addition de façon plus concise. Pour un flux d’entiers vide, cette somme fondée sur une identité vaut zéro. Avec des valeurs int, les calculs suivent les règles de dépassement de capacité des entiers de Java.

Trouver le minimum et le maximum avec reduce()

Pour calculer un minimum, Integer.MAX_VALUE joue le rôle d’identité pour Math.min ; pour un maximum, Integer.MIN_VALUE joue ce rôle pour Math.max. Ce sont des valeurs sentinelles choisies afin qu’une valeur réelle du flux les remplace pendant la réduction. L’exemple complet suivant calcule les deux résultats :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] numbers = {45, 56, 87, 323, 47, 20, 658, 985, 78, 123};
        int min = Arrays.stream(numbers).reduce(Integer.MAX_VALUE, Math::min);
        int max = Arrays.stream(numbers).reduce(Integer.MIN_VALUE, Math::max);
        System.out.println("min: " + min);
        System.out.println("max: " + max);
    }
}

Si le flux est vide, ces réductions fondées sur une identité renvoient Integer.MAX_VALUE pour le minimum et Integer.MIN_VALUE pour le maximum. Ces valeurs sentinelles ne sont pas de véritables extrêmes des données d’entrée. Si l’appelant doit distinguer l’absence de valeur d’un résultat réel, utilisez l’opération min() ou max(), dont le résultat pour un IntStream est un OptionalInt.

Quand utiliser reduce() ?

Utilisez reduce() lorsque vous avez besoin d’une règle générale de combinaison dotée d’une identité valide et d’un accumulateur associatif. Pour calculer une somme ordinaire d’entiers, IntStream.sum() exprime plus clairement l’intention ; min() et max() précisent aussi l’opération souhaitée et représentent l’absence de valeur avec OptionalInt. Choisissez l’opération spécialisée lorsqu’elle correspond à la tâche. Utilisez reduce() lorsque la règle de combinaison elle-même est l’objectif, et vérifiez que les conditions sur l’identité et l’associativité sont respectées avant d’utiliser un flux parallèle.