Stream.reduce în Java

Combină toate elementele într-o singură valoare, aplicând o operație pas cu pas (sumă, produs, maxim, concatenare).

Sintaxă
T reduce(T identitate, BinaryOperator<T> combina)
Optional<T> reduce(BinaryOperator<T> combina)
<U> U reduce(U identitate, BiFunction<U, ? super T, U> acumuleaza, BinaryOperator<U> combina)

Ce face Stream.reduce

reduce(identitate, f) pornește de la identitate și o combină cu fiecare element: rezultat = f(rezultat, element). Pentru sumă, identitatea este 0 și funcția Integer::sum; pentru produs, 1 și (a, b) -> a * b.

Varianta fără identitate returnează un Optional, gol dacă stream-ul nu are elemente. Varianta cu trei argumente permite un rezultat de alt tip decât elementele (de exemplu, suma prețurilor dintr-un stream de produse).

Operația trebuie să fie asociativă ((a+b)+c == a+(b+c)), iar identitatea trebuie să nu schimbe nimic (0 pentru sumă). Altfel, pe stream-uri paralele rezultatul iese greșit.

Parametri

Parametru Descriere
identitate Valoarea de pornire și rezultatul pentru un stream gol.
combina / acumuleaza Funcția care unește rezultatul parțial cu următorul element.

Ce returnează

Valoarea combinată (de tip T sau U) sau un Optional<T> pentru varianta fără identitate.

Exemple

Exemplul 1: Sumă, produs, maxim

import java.util.List;
import java.util.Optional;

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

        int suma = numere.stream().reduce(0, Integer::sum);
        int produs = numere.stream().reduce(1, (a, b) -> a * b);
        Optional<Integer> maxim = numere.stream().reduce(Integer::max);

        System.out.println("Sumă: " + suma);
        System.out.println("Produs: " + produs);
        System.out.println("Maxim: " + maxim.orElseThrow());
        System.out.println("Gol: " + List.<Integer>of().stream().reduce(Integer::max));
    }
}

Rezultat

Sumă: 18
Produs: 240
Maxim: 8
Gol: Optional.empty

Exemplul 2: Rezultat de alt tip decât elementele

Elementele sunt produse, rezultatul este un double. Al treilea argument unește sumele parțiale (folosit în paralel).

import java.util.List;

public class Main {
    record Produs(String nume, double pret) {}

    public static void main(String[] args) {
        List<Produs> cos = List.of(new Produs("Caiet", 12.5), new Produs("Carte", 45.0), new Produs("Pix", 4.5));
        double total = cos.stream()
                .reduce(0.0, (suma, p) -> suma + p.pret(), Double::sum);
        System.out.println("Total: " + total + " lei");
    }
}

Rezultat

Total: 62.0 lei

Exemplul 3: Același rezultat în paralel

Adunarea este asociativă și 0 este identitatea, deci suma este aceeași cu sau fără parallel().

import java.util.stream.LongStream;

public class Main {
    public static void main(String[] args) {
        long secvential = LongStream.rangeClosed(1, 1_000_000).reduce(0, Long::sum);
        long paralel = LongStream.rangeClosed(1, 1_000_000).parallel().reduce(0, Long::sum);
        System.out.println(secvential);
        System.out.println(paralel);
        System.out.println("Egale: " + (secvential == paralel));
    }
}

Rezultat

500000500000
500000500000
Egale: true

Greșeli frecvente

  • Dai o identitate greșită, de exemplu reduce(10, Integer::sum) pentru „sumă plus 10”. În paralel, 10 se adaugă o dată pentru fiecare bucată; adaugă 10 după reduce.
  • Folosești reduce cu o operație neasociativă, ca scăderea. Rezultatul diferă în paralel.
  • Folosești reduce pentru a construi liste sau texte lungi. Este lent; folosește collect/Collectors.joining.

Comenzi înrudite

  • Stream.collect: Adună elementele într-un rezultat (listă, set, map, text) folosind un Collector, de obicei din clasa Collectors.
  • BinaryOperator: O funcție care combină două valori de același tip într-una (T, T → T); folosită de reduce, toMap și Map.merge.
  • IntStream.sum: sum adună numerele din IntStream; average dă media lor ca OptionalDouble (gol dacă nu există numere).
  • Collectors.reducing: Varianta de colector a lui reduce: combină elementele două câte două; util ca downstream în groupingBy.
  • Stream.min / Stream.max: Găsesc cel mai mic și cel mai mare element după un Comparator; rezultatul este un Optional.