Java 8 – Create custom streams collector (Collect multiple values)

For Java 8 streams, there are several inbuilt collectors available like java.util.stream.Collectors.toList(), java.util.stream.Collectors.summarizingInt() etc. This article will explain how to create your own custom collector. This can also be used to collect/return multiple values from streams.

Example problem:

  • Consider we have employee class having name, role & department.
  • From the list of such employees, we have to get –
    • Count of Employees with role “Lead”
    • Count of employees with department “Sales”
  • This should be achieved in single stream iteration using custom collector.

Example implementation:

Lets create simple Employee object. For simplicity we will omit getter/setter methods.

 

As a final result, we will be expecting single object having both the counts. Output class will be like this.

 

Now lets create a collector which can convert list of employees into EmployeeStats with counts.

 

Explanation of EmployeeStatsCollector

java.util.stream.Collector expects below mandatory methods implemented. Lets look at their meaning from our example perspective.

  • supplier
    • java.util.function.Supplier that can provide new instance of EmployeeStats.
    • We simply pass a supplier which provides new instance of EmployeeStatsusing “new” operator.
  • accumulator
    • java.util.function.BiConsumer that will take EmployeeStats & Employee objects & updates statistics into EmployeeStats as per values in Employee object.
    • We create BiConsumer which takes 2 method arguments EmployeeStats & Employee.
    • Based on role & department of employee we increase leadEmployeeCount & salesDepartmentEmployeeCount in EmployeeStat.
  • combiner
    • This is somewhat similar to accumulator. Accumulator updates EmployeeStats instance from Employee object. Combiner updated EmployeeStats instance from another EmployeeStats instance.
    • We create BinaryOperator which takes 2 EmployeeStats.
    • We add values from other EmployeeStats into _this EmployeeStats & return _this which will have added values of both EmployeeStats instances.
  • finisher
    • This is for final transformation.
    • For our example we will just return existing EmployeeStats instance as it is without any changes.
  • characteristics – For our example we will use Collector.Characteristics.IDENTITY_FINISH

How Java Stream uses collector:

Now Java stream API might use above implementations in two ways.

  1. First create new instance of EmployeeStats using supplier
  2. Then call accumulator for each Employee object one by one & pass earlier EmployeeStats object in each call.
  3. At the end, call finisher & get final EmployeeStats result.

Or

  1. First create new instance of EmployeeStats using supplier
  2. Then call accumulator for some Employee object one by one & pass earlier EmployeeStats object in each call.
  3. Create another new instance of EmployeeStats using supplier
  4. Then call accumulator for remaining Employee object one by one & pass earlier another EmployeeStats object in each call.
  5. At the end, call combiner to combine both EmployeeStats objects  & get final EmployeeStats result.

Testing EmployeeStatsCollector:

Now lets test our accumulator.

Output:

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *