groupingBy

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • Collectors.groupingBy() is used within a collect() terminal operation to group elements of a stream into a Map.
  • It functions similarly to a GROUP BY clause in SQL.
  • The resulting Map uses the classification property as the Key, and a List of matching objects as the Value.

Concept

If you have a list of Employee objects, and you want to group them by Department, doing this manually requires loops, if-else blocks, and manual Map instantiation.

groupingBy(Function) automates this. You provide a function that extracts the classification key (e.g., Employee::getDepartment). It returns a Map<String, List<Employee>> where the keys are the departments, and the values are lists of employees belonging to that department.

Examples

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

class Employee {
    String name;
    String department;
    
    public Employee(String n, String d) { name = n; department = d; }
    public String getDepartment() { return department; }
    public String getName() { return name; }
    
    @Override
    public String toString() { return name; }
}

public class GroupingByExample {
    public static void main(String[] args) {
        
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", "HR"),
            new Employee("Bob", "IT"),
            new Employee("Charlie", "HR"),
            new Employee("David", "IT")
        );
        
        // 1. Basic Grouping
        Map<String, List<Employee>> byDept = employees.stream()
            .collect(Collectors.groupingBy(Employee::getDepartment));
            
        System.out.println(byDept); 
        // {HR=[Alice, Charlie], IT=[Bob, David]}
        
        
        // 2. Grouping and Counting (Downstream Collector)
        Map<String, Long> countByDept = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::getDepartment, 
                Collectors.counting() // Downstream collector
            ));
            
        System.out.println(countByDept); 
        // {HR=2, IT=2}
    }
}

Interview Questions

Q: What is a “Downstream Collector” in groupingBy?
A: By default, groupingBy(classifier) collects the grouped elements into a List. If you want to change what happens to those grouped elements, you pass a second argument called a Downstream Collector. For example, Collectors.groupingBy(Employee::getDept, Collectors.counting()) will group them by department, but instead of returning a List of employees, it counts them and returns a Map<String, Long>.

Q: Can you group by multiple fields?
A: Yes. You can nest groupingBy collectors inside each other. If you want to group by Department, and then by Gender within that department, you would write:
Collectors.groupingBy(Employee::getDept, Collectors.groupingBy(Employee::getGender))
This would result in a Map<String, Map<String, List<Employee>>>.