Mastering Grouping in Java Streams: The Power of Collectors.groupingBy()

Introduction

Grouping data is a common task in software development. You might need to organize employees by department, group products by category, or aggregate transactions by status.

Java Streams make this straightforward with Collectors.groupingBy().

What Is groupingBy()?

groupingBy() is a collector that partitions stream elements into a Map based on a classification function.

In simple terms:

  • You provide a rule to classify each element.
  • Java creates groups for each key.
  • The result is usually a map like Map<Key, List<Value>>.

Example: Grouping Employees by Department

Imagine we have a list of employees and want to organize them by department.

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

public class GroupingExample {
  record Employee(String name, String department) {}

  public static void main(String[] args) {
    List<Employee> employees = List.of(
      new Employee("Alice", "IT"),
      new Employee("Bob", "HR"),
      new Employee("Charlie", "IT"),
      new Employee("David", "HR"),
      new Employee("Eve", "Finance")
    );

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

    System.out.println(employeesByDepartment);
  }
}

Expected output:

{
  IT=[Employee[name=Alice, department=IT], Employee[name=Charlie, department=IT]],
  HR=[Employee[name=Bob, department=HR], Employee[name=David, department=HR]],
  Finance=[Employee[name=Eve, department=Finance]]
}

Example: Grouping and Counting

If you only need totals per group, combine groupingBy() with Collectors.counting().

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

System.out.println(departmentCounts);

Expected output:

{IT=2, HR=2, Finance=1}

Example: Grouping with a Downstream Collector

You can transform grouped values while collecting. For example, keep only employee names in each department:

Map<String, List<String>> departmentEmployeeNames = employees.stream()
  .collect(Collectors.groupingBy(
    Employee::department,
    Collectors.mapping(Employee::name, Collectors.toList())
  ));

System.out.println(departmentEmployeeNames);

Expected output:

{IT=[Alice, Charlie], HR=[Bob, David], Finance=[Eve]}

When Should You Use groupingBy()?

Use groupingBy() when you need to:

  • Categorize a collection into logical groups.
  • Aggregate values per category (for example, counts).
  • Transform grouped values with downstream collectors such as mapping() and counting().

Conclusion

Collectors.groupingBy() is one of the most useful tools in the Java Streams API. It helps you write concise, expressive code for common grouping and aggregation tasks.

Once you are comfortable with basic grouping, explore nested grouping and custom downstream collectors to build even richer data transformations.