Learnitweb

Collectors.groupingBy

Collectors.groupingBy is a powerful collector in Java Streams used to group elements of a stream based on a classifier function. It is commonly used for aggregation tasks like counting, summing, or finding max/min per group.

1. Basic Concept

Collectors.groupingBy groups stream elements based on a classifier function:

Map<K, List<T>> groupedMap = stream.collect(Collectors.groupingBy(classifierFunction));
  • K is the type of the grouping key.
  • T is the type of stream elements.
  • The result is a Map<K, List<T>> by default.

2. Example 1: Group Strings by Length

List<String> words = Arrays.asList("Java", "Stream", "API", "Lambda", "Code");

Map<Integer, List<String>> groupedByLength = words.stream()
    .collect(Collectors.groupingBy(String::length));

groupedByLength.forEach((length, list) -> 
    System.out.println(length + ": " + list));

Output:

3: [API]
4: [Java, Code]
6: [Stream, Lambda]

Explanation:

  • String::length is the classifier function.
  • Strings are grouped into lists based on their length.

3. Example 2: Group Employees by Department

Assume an Employee class:

public class Employee {
    private String name;
    private String department;
    private double salary;

    public Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }
}

And a list of employees:

List<Employee> employees = Arrays.asList(
    new Employee("Alice", "HR", 50000),
    new Employee("Bob", "IT", 75000),
    new Employee("Charlie", "IT", 80000),
    new Employee("David", "HR", 60000)
);

Group by Department

Map<String, List<Employee>> employeesByDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment));

employeesByDept.forEach((dept, list) -> {
    System.out.print(dept + ": ");
    list.forEach(emp -> System.out.print(emp.getName() + " "));
    System.out.println();
});

Output:

HR: Alice David 
IT: Bob Charlie 

Explanation:

  • Employee::getDepartment is the classifier function.
  • Employees are grouped into lists based on their department.

4. Example 3: Group and Count Elements

You can combine groupingBy with downstream collectors like counting():

Map<String, Long> countByDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));

countByDept.forEach((dept, count) -> System.out.println(dept + ": " + count));

Output:

HR: 2
IT: 2

Explanation:

  • Collectors.counting() counts the number of employees in each group.
  • The result is a Map<String, Long>.

5. Example 4: Group and Find Max Salary Per Department

Map<String, Optional<Employee>> highestPaid = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.maxBy(Comparator.comparing(Employee::getSalary))
    ));

highestPaid.forEach((dept, empOpt) -> empOpt.ifPresent(emp ->
    System.out.println(dept + ": " + emp.getName() + " - " + emp.getSalary())
));

Output:

HR: David - 60000.0
IT: Charlie - 80000.0

Explanation:

  • Collectors.maxBy() finds the employee with the highest salary in each department.
  • The result is a Map<String, Optional<Employee>>.

6. Example 5: Group and Transform the Result

You can transform the downstream result using collectingAndThen:

Map<String, Employee> highestPaid2 = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.collectingAndThen(
            Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
            Optional::get
        )
    ));

highestPaid2.forEach((dept, emp) -> 
    System.out.println(dept + ": " + emp.getName() + " - " + emp.getSalary()));

Output:

HR: David - 60000.0
IT: Charlie - 80000.0

Note:

  • Optional::get throws an exception if the group is empty. Use only if you are sure groups are non-empty or handle safely.

7. Summary

  • Collectors.groupingBy(classifier) groups elements by a key.
  • Can be combined with downstream collectors like counting(), maxBy(), summarizingDouble().
  • collectingAndThen allows post-processing, like unwrapping Optional or transforming the result.
  • Useful for aggregation tasks, reporting, or statistics per group.