Collectors.reducing în Java

Varianta de colector a lui reduce: combină elementele două câte două; util ca downstream în groupingBy.

Sintaxă
static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
static <T> Collector<T, ?, T> reducing(T identitate, BinaryOperator<T> op)
static <T, U> Collector<T, ?, U> reducing(U identitate, Function<T, U> f, BinaryOperator<U> op)

Ce face Collectors.reducing

reducing face același lucru ca Stream.reduce: pornește de la o valoare inițială (identitatea) și combină pe rând elementele cu o operație, de exemplu adunarea. Folosit direct, reduce este mai simplu.

Utilitatea apare în groupingBy: „cea mai lungă denumire pe categorie” sau „produsul notelor pe grupă”. Varianta cu trei argumente transformă întâi fiecare element cu f și abia apoi combină. Pentru sume, numărări și maxime există colectori dedicați, mai clari (summingInt, counting, maxBy).

Parametri

Parametru Descriere
identitate Valoarea de pornire (0 pentru sumă, 1 pentru produs, „” pentru texte).
f Opțional: transformarea aplicată fiecărui element înainte de combinare.
op Operația care unește două valori într-una.

Ce returnează

Un Collector cu rezultatul combinării (un Optional în varianta fără identitate).

Exemple

Exemplul 1: Cel mai lung nume de oraș din fiecare județ

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

public class Main {
    record Localitate(String nume, String judet) {}

    public static void main(String[] args) {
        List<Localitate> lista = List.of(
                new Localitate("Iași", "IS"), new Localitate("Pașcani", "IS"),
                new Localitate("Hârlău", "IS"), new Localitate("Cluj-Napoca", "CJ"), new Localitate("Dej", "CJ"));

        BinaryOperator<String> maiLung = (a, b) -> b.length() > a.length() ? b : a;
        Map<String, String> rezultat = lista.stream()
                .collect(Collectors.groupingBy(Localitate::judet, TreeMap::new,
                        Collectors.reducing("", Localitate::nume, maiLung)));
        System.out.println(rezultat);
    }
}

Rezultat

{CJ=Cluj-Napoca, IS=Pașcani}

Exemplul 2: reducing cu și fără identitate

Fără valoare inițială rezultatul este un Optional, gol pentru o listă goală.

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Integer> zile = List.of(3, 5, 2);

        int total = zile.stream().collect(Collectors.reducing(0, Integer::sum));
        Optional<Integer> produs = zile.stream().collect(Collectors.reducing((a, b) -> a * b));
        Optional<Integer> gol = List.<Integer>of().stream().collect(Collectors.reducing(Integer::sum));

        System.out.println("Total zile de concediu: " + total);
        System.out.println("Produs: " + produs.orElse(0));
        System.out.println("Listă goală: " + gol);
    }
}

Rezultat

Total zile de concediu: 10
Produs: 30
Listă goală: Optional.empty

Greșeli frecvente

  • Dai o identitate greșită, de exemplu 1 pentru o sumă: fiecare grup primește în plus valoarea inițială.
  • Folosești reducing pentru sume sau numărări. summingInt și counting se citesc mai ușor.

Comenzi înrudite

  • Stream.reduce: Combină toate elementele într-o singură valoare, aplicând o operație pas cu pas (sumă, produs, maxim, concatenare).
  • Collectors.groupingBy: Împarte elementele în grupuri după o cheie (oraș, grupă, prima literă) și dă un Map cheie → listă sau cheie → rezultat.
  • BinaryOperator: O funcție care combină două valori de același tip într-una (T, T → T); folosită de reduce, toMap și Map.merge.
  • Collectors.minBy: Găsește elementul cel mai mic (minBy) sau cel mai mare (maxBy) după un Comparator; util pe grupuri, cu groupingBy.