Complete Guide to Collectors Utility in Java

Introduction

In this guide we will learn how to use Collectors Utility in Java.

It provides implementations of Collector that implement various useful reduce operations such as accumulating elements into collections, summarizing elements according to various criteria.

Let us check how to use Collectors utility with examples.

Consider a employee class as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class Employee {

    private String empId;

    private String empName;

    private int salary;

    private String deptName;

    private int age;

    public Employee(String empId, String empName
     , int salary, String deptName, int age) {
        this.empId = empId;
        this.empName = empName;
        this.salary = salary;
        this.deptName = deptName;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "empId='" + empId + '\'' +
                ", empName='" + empName + '\'' +
                ", salary=" + salary +
                ", deptName='" + deptName + '\'' +
                ", age=" + age +
                '}';
    }

    public String getEmpId() {
        return empId;
    }

    public void setEmpId(String empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Employee> employees = new ArrayList<>();
Employee emp1 = new Employee("1","Tim",10000,"Sales",30);
Employee emp2 = new Employee("2","Jim",5000,"Developer",25);
Employee emp3 = new Employee("3","Zara",8000,"Developer",33);
Employee emp4 = new Employee("4","Mary",11000,"Marketing",40);
Employee emp5 = new Employee("5","Rocky",12000,"Developer",39);

employees.add(emp1);
employees.add(emp2);
employees.add(emp3);
employees.add(emp4);
employees.add(emp5);

Collectors.toList: Collecting processed elements into different collection

Using Collectors.toList() we can collect processed elements into different collection i.e List

1
2
3
4
5
6
7
List<Employee> filteredEmployee =
employees.stream()
         .filter(e -> e.getSalary() > 10000)
         .collect(Collectors.toList());
System.out.println(filteredEmployee);

//[Employee{empId='4', empName='Mary', salary=11000, deptName='Marketing', age=40}, Employee{empId='5', empName='Rocky', salary=12000, deptName='Developer', age=39}]

If we want to collect to read only collection which cannot be modified we can use Collectors.toUnmodifiableList().

1
2
3
4
5
6
7
List<Employee> filteredEmployee =
    employees.stream()
             .filter(e -> e.getSalary() > 10000)
             .collect(Collectors.toUnmodifiableList());
System.out.println(filteredEmployee);

//[Employee{empId='4', empName='Mary', salary=11000, deptName='Marketing', age=40}, Employee{empId='5', empName='Rocky', salary=12000, deptName='Developer', age=39}]

Collectors.groupingBy: Grouping collection by department

We can group elements in the collection by using department name by using Collectors.groupingBy().

It returns a Map<String, List> where key represents groupid(department name) and list contains employees belonging to that group.

1
2
3
4
5
6
7
8
Map<String, List<Employee>> group = 
       employees.stream()
                .collect(Collectors.groupingBy(e -> e.getDeptName()));
System.out.println(group.keySet());
System.out.println(group);

//[Sales, Developer, Marketing]
// {Sales=[Employee{empId='1', empName='Tim', salary=10000, deptName='Sales', age=30}], Developer=[Employee{empId='2', empName='Jim', salary=5000, deptName='Developer', age=25}, Employee{empId='3', empName='Zara', salary=8000, deptName='Developer', age=33}, Employee{empId='5', empName='Rocky', salary=12000, deptName='Developer', age=39}], Marketing=[Employee{empId='4', empName='Mary', salary=11000, deptName='Marketing', age=40}]}

Collectors.summarizingInt: Summary statistics of the resulting values

Using Collectors.summarizingInt() we can get useful statistics for the input values processed by the stream.

In the below example we would gather statistics for the salary of the employee.

1
2
3
4
5
6
7
8
9
IntSummaryStatistics statistics =
      employees
            .stream()
            .collect(Collectors.summarizingInt(s -> s.getSalary()));

System.out.println("Count: "+statistics.getCount()+ " Average: "+statistics.getAverage()
+ " Max: "+statistics.getMax() + " Min: "+statistics.getMin()+ " Sum: "+statistics.getSum());

// Count: 5 Average: 9200.0 Max: 12000 Min: 5000 Sum: 46000

Collectors.partitioningBy: Partioning input elements based on a Predicate

To partition input elements into 2 groups based on a predicate we can use Collectors.partitioningBy.

One group contains values which have met predicate condition and other which has not.

In the below example employees are partitioned into 2 groups based on salary > 10000

1
2
3
4
5
6
7
8
Map<Boolean, List<Employee>> partitionBySalary = 
        employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 10000));
System.out.println("True: "+partitionBySalary.get(true));
System.out.println("False: "+partitionBySalary.get(false));

//True: [Employee{empId='4', empName='Mary', salary=11000, deptName='Marketing', age=40}, Employee{empId='5', empName='Rocky', salary=12000, deptName='Developer', age=39}]
//False: [Employee{empId='1', empName='Tim', salary=10000, deptName='Sales', age=30}, Employee{empId='2', empName='Jim', salary=5000, deptName='Developer', age=25}, Employee{empId='3', empName='Zara', salary=8000, deptName='Developer', age=33}]

Collectors.joining: Joining input elements processed by a delimiter

We can concatenate the input elements processed by a given delimiter using Collectors.joining() method.

In the below example we take employee name from the employee object and concatenated them using delimiter ‘,’.

1
2
3
4
5
6
7
8
String joinedEmployeeNames = 
            employees
                    .stream()
                    .map(Employee::getEmpName)
                    .collect(Collectors.joining(","));
System.out.println(joinedEmployeeNames);

// Tim,Jim,Zara,Mary,Rocky

Collectors.toCollection: Accumulate input elements into TreeSet

We can sort the input elements processed by Stream by storing them into TreeSet.

To sort elements we need to specify Comparator as an argument to the TreeSet.

In the below example we are sorting by employee id.

1
2
3
4
5
6
7
8
TreeSet<Employee> sortedEmployee =
        employees.stream()
                  .collect(Collectors.toCollection(() ->
                  new TreeSet<>(Comparator.comparing(e->e.getEmpId()))));

System.out.println(sortedEmployee);

//[Employee{empId='1', empName='Tim', salary=10000, deptName='Sales', age=30}, Employee{empId='2', empName='Jim', salary=5000, deptName='Developer', age=25}, Employee{empId='3', empName='Zara', salary=8000, deptName='Developer', age=33}, Employee{empId='4', empName='Mary', salary=11000, deptName='Marketing', age=40}, Employee{empId='5', empName='Rocky', salary=12000, deptName='Developer', age=39}]