In Java 8, the Collectors.groupingBy()
method in the Stream API is used to group elements of the stream into a `Map“ based on a categorization function.
Here’s a basic example:
package org.kodejava.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CollectorsGroupingBy {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Alice", "Rosa", "Tom", "John");
Map<String, List<String>> groupedByName = names.stream()
.collect(Collectors.groupingBy(Function.identity()));
groupedByName.forEach((name, nameList) -> {
System.out.println("Name : " + name + " Count : " + nameList.size());
});
}
}
Output:
Name : Tom Count : 1
Name : Alice Count : 1
Name : John Count : 2
Name : Rosa Count : 1
In this case, elements of the names
list are grouped by their identity (Function.identity()
returns a function that returns its input). So the resulting Map
has the name as a key, and a list of names as the value. If a name appears more than once in the list, it will appear more than once in the corresponding list in the Map
.
groupingBy()
can also be used with more complex streams. For example, if you have a stream of Employee
objects, and you want to group employees by their department, you can do it like:
package org.kodejava.stream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class EmployeeGroupingBy {
public static void main(String[] args) {
// Create a list of employees
List<Employee> employees = Arrays.asList(
new Employee("John", 25, "Finance"),
new Employee("Sarah", 28, "Marketing"),
new Employee("Tom", 35, "IT"),
new Employee("Rosa", 30, "Finance"),
new Employee("Sam", 24, "IT"));
// Group the employees by their department
Map<String, List<Employee>> employeesByDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println(employeesByDepartment);
}
}
class Employee {
private final String name;
private final int age;
private final String department;
public Employee(String name, int age, String department) {
this.name = name;
this.age = age;
this.department = department;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getDepartment() {
return department;
}
@Override
public String toString() {
return "name='" + name + "'";
}
}
Output:
{Finance=[name='John', name='Rosa'], IT=[name='Tom', name='Sam'], Marketing=[name='Sarah']}
Collectors.groupingBy()
is very flexible and can be used with additional parameters to provide more control over how the grouping is done, including changing the type of Map
returned, modifying how the values are collected, or using a secondary groupingBy()
call to create a multi-level Map.
In the above example, the groupingBy()
method groups the employees by their department. The department field of the Employee
object is used as the key of the Map
and the value is the list of employees in that department.
The Employee::getDepartment
in the groupingBy()
is a method reference in Java. It’s equivalent to writing employee -> employee.getDepartment()
. The ::
is used to reference a method or a constructor in the Java class.
Here, Employee::getDepartment
is used as a classifier function that applies to each element in the stream. So groupingBy()
distributes elements of the stream into groups according to the value returned by this function.