Ce face Collectors.groupingBy
groupingBy(Student::oras) pune fiecare student în grupul orașului lui și returnează un Map<String, List<Student>>. Funcția dată se numește clasificare: ea spune în ce grup intră elementul.
Al doilea argument (colectorul downstream) spune ce se face cu fiecare grup în loc de o listă: counting() numără, averagingDouble(...) face media, mapping(...) păstrează doar un câmp, maxBy(...) alege maximul.
Map-ul returnat este un HashMap: ordinea cheilor la afișare nu este garantată. Pentru un rezultat stabil, folosește varianta cu trei argumente și dă TreeMap::new (chei sortate) sau învelește rezultatul în new TreeMap<>(...).
Parametri
| Parametru | Descriere |
|---|---|
clasificare |
Funcția care dă cheia grupului pentru fiecare element. |
fabricaMap |
Opțional: ce map se creează, de exemplu TreeMap::new. |
downstream |
Opțional: colectorul aplicat fiecărui grup (implicit toList()). |
Ce returnează
Un Collector care produce un Map de la cheia grupului la lista elementelor sau la rezultatul colectorului downstream.
Exemple
Exemplul 1: Studenții grupați pe oraș
Cu TreeMap::new orașele apar în ordine alfabetică. Ultimul argument, Collectors.toList(), este cel implicit, dar trebuie scris în varianta cu trei argumente.
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class Main {
record Student(String nume, String oras) {}
public static void main(String[] args) {
List<Student> studenti = List.of(
new Student("Ana", "Iași"), new Student("Radu", "Cluj"),
new Student("Ioana", "Iași"), new Student("Dan", "Brașov"), new Student("Elena", "Cluj"));
Map<String, List<Student>> peOras = studenti.stream()
.collect(Collectors.groupingBy(Student::oras, TreeMap::new, Collectors.toList()));
peOras.forEach((oras, lista) -> System.out.println(oras + ": " + lista.size() + " " + lista));
}
}
Rezultat
Brașov: 1 [Student[nume=Dan, oras=Brașov]]
Cluj: 2 [Student[nume=Radu, oras=Cluj], Student[nume=Elena, oras=Cluj]]
Iași: 2 [Student[nume=Ana, oras=Iași], Student[nume=Ioana, oras=Iași]]
Exemplul 2: Grupare cu downstream: numele pe prima literă
mapping(...) păstrează din fiecare grup doar textul, iar new TreeMap<>(...) sortează literele.
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> nume = List.of("Ana", "Andrei", "Bogdan", "Maria", "Mihai", "Alina");
Map<Character, String> dupaLitera = new TreeMap<>(nume.stream()
.collect(Collectors.groupingBy(n -> n.charAt(0), Collectors.joining(", "))));
System.out.println(dupaLitera);
Map<Integer, Long> dupaLungime = nume.stream()
.collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.counting()));
System.out.println("Câte nume au fiecare lungime: " + dupaLungime);
}
}
Rezultat
{A=Ana, Andrei, Alina, B=Bogdan, M=Maria, Mihai}
Câte nume au fiecare lungime: {3=1, 5=3, 6=2}
Exemplul 3: Grupări pe două niveluri: județ și apoi gen
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class Main {
record Persoana(String nume, String judet, char gen) {}
public static void main(String[] args) {
List<Persoana> lista = List.of(
new Persoana("Ana", "Iași", 'F'), new Persoana("Radu", "Iași", 'M'),
new Persoana("Ioana", "Iași", 'F'), new Persoana("Dan", "Cluj", 'M'));
Map<String, Map<Character, Long>> rezultat = lista.stream()
.collect(Collectors.groupingBy(Persoana::judet, TreeMap::new,
Collectors.groupingBy(Persoana::gen, TreeMap::new, Collectors.counting())));
System.out.println(rezultat);
}
}
Rezultat
{Cluj={M=1}, Iași={F=2, M=1}}
Greșeli frecvente
- Afișezi direct map-ul din
groupingByși te miri că ordinea se schimbă de la un set de date la altul. Cere unTreeMap. - Funcția de clasificare returnează
nullpentru un element:groupingByaruncăNullPointerException(„element cannot be mapped to a null key”). - Scrii
groupingBy(f, TreeMap::new)cu doar două argumente: nu compilează. Cu fabrica de map trebuie dat și colectorul downstream.
Comenzi înrudite
Collectors.partitioningBy: Împarte elementele în două grupuri, după o condiție: cele care o îndeplinesc (true) și celelalte (false).Collectors.counting: Numără elementele; folosit mai ales în groupingBy, pentru „câte elemente are fiecare grup”.Collectors.mapping: Transformă fiecare element înainte să-l dea altui colector; folosit în groupingBy ca să păstrezi doar un câmp.Collectors.toMap: Construiește un Map din stream: o funcție dă cheia, alta valoarea; o a treia spune ce faci când cheia se repetă.Collectors: Clasa cu rețete gata făcute pentru collect: liste, mulțimi, map-uri, texte unite, grupări, numărări și medii.