Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to group a list by a field

Short answer

Collectors.groupingBy() collects elements into a Map where the key is the field value and the value is a list of elements:

record Employee(String name, String department) {}
 
void main() {
    var staff = List.of(
            new Employee("Anna", "IT"),
            new Employee("Oleg", "Sales"),
            new Employee("Boris", "IT"));
 
    Map<String, List<Employee>> byDepartment = staff.stream()
            .collect(Collectors.groupingBy(Employee::department));
    IO.println(byDepartment.get("IT"));
}

Output:

[Employee[name=Anna, department=IT], Employee[name=Boris, department=IT]]

Sorted keys

By default the result is a HashMap with no order. Pass TreeMap::new to get keys in alphabetical order:

void main() {
    var words = List.of("apple", "bob", "avocado", "cat", "banana");
 
    var byLetter = words.stream()
            .collect(Collectors.groupingBy(w -> w.charAt(0), TreeMap::new, Collectors.toList()));
    IO.println(byLetter);
}

Output:

{a=[apple, avocado], b=[bob, banana], c=[cat]}

Only the field you need

mapping() transforms the group's elements before collecting:

record Employee(String name, String department) {}
 
void main() {
    var staff = List.of(new Employee("Anna", "IT"), new Employee("Oleg", "Sales"), new Employee("Boris", "IT"));
 
    var names = staff.stream().collect(Collectors.groupingBy(Employee::department, TreeMap::new,
            Collectors.mapping(Employee::name, Collectors.toList())));
    IO.println(names);
}

Output:

{IT=[Anna, Boris], Sales=[Oleg]}

Counting per group

counting() gives the number of elements instead of a list:

void main() {
    var words = List.of("one", "two", "three", "four", "five", "six");
 
    var byLength = words.stream()
            .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.counting()));
    IO.println(byLength);
}

Output:

{3=3, 4=2, 5=1}

Common mistake

Grouping by a field that can be null. groupingBy() does not accept null as a key:

record Employee(String name, String department) {}
 
void main() {
    var staff = List.of(new Employee("Anna", "IT"), new Employee("Ivan", null));
    staff.stream().collect(Collectors.groupingBy(Employee::department));
}

Output:

Exception in thread "main" java.lang.NullPointerException: element cannot be mapped to a null key

Substitute a default value: e -> Objects.requireNonNullElse(e.department(), "none").

Functions used in this recipe

Thanks for reading!

Donate to the site — we work hard for you.