Open In App

How to Build a Custom Collector in Java?

Improve
Improve
Like Article
Like
Save
Share
Report

Java Collector is the utility class that provides many helpful methods and functions for the collector interface. Mainly collector implementation is used with the stream collect() method. The collector interface was provided by Java 8 under the part of the newly introduced Java Stream API. This interface provides various methods to perform mutual reduction operations.  The Mutable reduction operations perform arithmetic functions on the mathematical information in a stream to find minimum, maximum, average, or accumulate elements of a Stream into a Set or concatenate all the String elements from the Stream.

Steps to Create Custom Collector

To write custom collectors, we’re supposed to implement the interface with the below-mentioned methods. The collection usually happens in four steps provided by Streams API.

  • Supplier(): This is the first step of the element collection process, and In this process, the container is created to hold the elements from the stream. The return type of this method is the supplier of container type.
  • Accumulator(): This is the second step in which each element is added into the container created by Supplier Step. In this method, we have to return a BiConsumer function that accepts the container and every element from the Stream.
  • Combiner(): This is the optional step, which is executed only when the stream is processed in a parallel era, and if the Stream is sequential, then this step will be skipped. The Combine step is used to combine all the elements into a single container. In this method, we’re supposed to return a Binary Operator function that combines two accumulated containers.
  • Finish(): It is the last step in the collection process. It will execute only when all the elements in the stream are successfully accumulated in the supplier container. In this method, we can return a function to transform the accumulated and combined container into the final output.

Other than these methods, we have the characteristics() method to specify the characteristics of the Collector and the return type Enum values of a set of characteristics.

Example

To illustrate this example, we’ll divide the code into three sections for better understanding. Let’s take an example in which we have a list of Employee objects, and we want to create a stream from it and collect the Employee objects as an immutable list of Triplets. Each of the triplets in the list will represent an employee, and it will have the age, first name, and last name of the employee.

Java




public class Employee_GFG {
    private long id;
    private String firstName;
    private String lastName;
    private int year;
  
    public Employee_GFG(long id, String firstName,
                        String lastName, int year)
    {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.year = year;
    }
  
    public long getId() { return id; }
  
    public void setId(long id) { this.id = id; }
  
    public String getFirstName() { return firstName; }
  
    public void setFirstName(String firstName)
    {
        this.firstName = firstName;
    }
  
    public String getLastName() { return lastName; }
  
    public void setLastName(String lastName)
    {
        this.lastName = lastName;
    }
  
    public int getYear() { return year; }
  
    public void setYear(int year) { this.year = year; }
}


In the example mentioned above, we’ve class named Employee_GFG consist private variable named id, firstname, lastname, year, Constructor, which initialize the variables and also have various functions setFirstName, getLastName, set LastName, getYear, setYear, etc. this function just set or get the values of the variable.

Now we have to implement the Collector interface. To create the class for collector methods for supplier, accumulator, and finisher as discussed above.

Java




package com.example.geeksforgeeks.customcollector;
  
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import org.javatuples.Triplet;
  
public class EmployeeCollector implements
  
    Collector<Employee,
              List<Triplet<Integer, String, String> >,
              List<Triplet<Integer, String, String> > > {
  
    public static EmployeeCollector toEmployeeList()
    {
        return new EmployeeCollector();
    }
  
    public Supplier<
        List<Triplet<Integer, String, String> > >
    supplier()
    {
        return ArrayList::new;
    }
  
    public BiConsumer<
        List<Triplet<Integer, String, String> >, Student>
    accumulator()
    {
        return (list, student)
                   -> list.add(
                       Triplet.with(student.getYear(),
                                    student.getFirstName(),
                                    student.getLastName()));
    }
  
    public BinaryOperator<
        List<Triplet<Integer, String, String> > >
    combiner()
    {
        return (list1, list2) ->
        {
            list1.addAll(list2);
            return list1;
        };
    }
  
    @Override
    public Function<
        List<Triplet<Integer, String, String> >,
        List<Triplet<Integer, String, String> > >
    finisher()
    {
        return Collections::unmodifiableList;
    }
  
    @Override public Set<Characteristics> characteristics()
    {
        return Set.of(Characteristics.UNORDERED);
    }
}


In the example mentioned above, we created a class named EmployeeCollector. Under that, we implemented a custom collector with triplet Integer, Integer, String, and written Supplier function that supplies a container to hold Stream elements and supplies a new ArrayList when it is invoked. BiConsumer function in this returning function gets the container, which is an ArrayList and a Student object from the stream, BinaryOperator function is combiner which takes to containers as arguments and returns one, last-second function name “Function”  the container which is an ArrayList of student triplets and returns an Unmodifiable List.

Now, we will use our collector on employee streams. We will use the toEmployeeList() function in the collect() method.

Java




public static void main(String[] args)
{
    List<Employee> Employee = List.of(
        new Employee(1, "samarth", "sharma", 2019),
        new Employee(2, "praful", "john", 2019),
        new Employee(3, "goutam", "verma", 2015),
        new Employee(4, "mohit", "verma", 2016));
  
    List<Triplet<Integer, String, String> > listOfTriples
        = Employee.stream().collect(
            EmployeeCollector.toEmployeeList());
    listOfTriples.forEach(System.out::println);
}


Output

[1,samarth,sharma,2019]
[2,praful,john,2019]
[3,goutam,verma,2015]
[4,mohit,verma,2016]

Above is desired output from the code, which consist list of an employee with first name, last name, year, and id.



Last Updated : 03 Dec, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads