Collectors.toMap în Java

Construiește un Map din stream: o funcție dă cheia, alta valoarea; o a treia spune ce faci când cheia se repetă.

Sintaxă
static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<T, K> cheie, Function<T, U> valoare)
static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<T, K> cheie, Function<T, U> valoare,
        BinaryOperator<U> combinare)
static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<T, K> cheie, Function<T, U> valoare,
        BinaryOperator<U> combinare, Supplier<M> fabricaMap)

Ce face Collectors.toMap

Collectors.toMap(cheie, valoare) transformă fiecare element într-o pereche cheie-valoare. De exemplu, din lista de studenți obții map-ul „nume → notă” cu toMap(Student::nume, Student::nota).

Dacă două elemente dau aceeași cheie, varianta cu două argumente aruncă IllegalStateException: Duplicate key. Ca să eviți asta, dai a treia funcție, care combină cele două valori: Integer::sum (aduni), Math::max (păstrezi maximul) sau (a, b) -> a (păstrezi prima).

Map-ul rezultat este un HashMap, fără ordine. Cu al patrulea argument alegi tipul: TreeMap::new pentru chei sortate sau LinkedHashMap::new pentru ordinea din stream.

Parametri

Parametru Descriere
cheie Funcția care dă cheia pentru fiecare element.
valoare Funcția care dă valoarea pentru fiecare element.
combinare Opțional: cum se unesc două valori cu aceeași cheie.
fabricaMap Opțional: creează map-ul, de exemplu TreeMap::new.

Ce returnează

Un Collector care produce un Map<K, U>.

Exemple

Exemplul 1: Nume → notă, cu cheile sortate

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

public class Main {
    record Student(String nume, int nota) {}

    public static void main(String[] args) {
        List<Student> grupa = List.of(new Student("Radu", 8), new Student("Ana", 10), new Student("Ioana", 9));

        Map<String, Integer> note = grupa.stream()
                .collect(Collectors.toMap(Student::nume, Student::nota, (a, b) -> a, TreeMap::new));
        System.out.println(note);
        System.out.println("Nota Anei: " + note.get("Ana"));
    }
}

Rezultat

{Ana=10, Ioana=9, Radu=8}
Nota Anei: 10

Exemplul 2: Chei repetate: suma vânzărilor pe oraș

Fără funcția de combinare, al doilea „Iași” ar opri programul cu Duplicate key. Cu Integer::sum, sumele se adună.

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

public class Main {
    record Vanzare(String oras, int lei) {}

    public static void main(String[] args) {
        List<Vanzare> vanzari = List.of(
                new Vanzare("Iași", 120), new Vanzare("Cluj", 300),
                new Vanzare("Iași", 80), new Vanzare("Cluj", 50));

        try {
            vanzari.stream().collect(Collectors.toMap(Vanzare::oras, Vanzare::lei));
        } catch (IllegalStateException e) {
            System.out.println("Eroare: " + e.getMessage());
        }

        Map<String, Integer> total = vanzari.stream()
                .collect(Collectors.toMap(Vanzare::oras, Vanzare::lei, Integer::sum, TreeMap::new));
        System.out.println("Total pe oraș: " + total);
    }
}

Rezultat

Eroare: Duplicate key Iași (attempted merging values 120 and 80)
Total pe oraș: {Cluj=350, Iași=200}

Greșeli frecvente

  • Nu tratezi cheile repetate. Dacă datele pot avea aceeași cheie de două ori, dă mereu funcția de combinare.
  • Funcția de valoare returnează null: toMap aruncă NullPointerException, chiar dacă HashMap acceptă valori null.
  • Folosești toMap ca să numeri sau să grupezi liste de elemente. Pentru asta, groupingBy este mai clar.

Comenzi înrudite

  • Collectors.groupingBy: Împarte elementele în grupuri după o cheie (oraș, grupă, prima literă) și dă un Map cheie → listă sau cheie → rezultat.
  • Collectors: Clasa cu rețete gata făcute pentru collect: liste, mulțimi, map-uri, texte unite, grupări, numărări și medii.
  • 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.