Java - Stream Collect Operation with Supplier and Accumulator

Introduction

You can use collectors to collect the results of executing a stream pipeline into a collection such as a List, a Set, a Map, etc.

collect() method of the Stream<T> interface does the job of collecting.

The following version of the collect() method takes three arguments:

  • Parameter Description
  • supplier supplies a mutable container to store (or collect) the results.
  • accumulator accumulates the results into the mutable container.
  • combiner combines the partial results when the reduction operation takes place in parallel.
<R> R collect(Supplier<R> supplier, 
              BiConsumer<R,? super T> accumulator, 
              BiConsumer<R,R> combiner)

Example

To collect the names of all of the people in an ArrayList<String>.

First, you need to have a supplier that will return an ArrayList<String> to store the names.

You can use either of the following statements to create the supplier:

// Using a lambda expression
Supplier<ArrayList<String>> supplier = () -> new ArrayList<>();

// Using a constructor reference
Supplier<ArrayList<String>> supplier = ArrayList::new;

Second, create an accumulator that receives two arguments.

The first argument is the container returned from the supplier, which is the ArrayList<String> in this case.

The second argument is the element of the stream.

Your accumulator should simply add the names to the list.

You can use either of the following statements to create an accumulator:

// Using a lambda expression
BiConsumer<ArrayList<String>, String> accumulator = (list, name) -> list.add(name);

// Using a constructor reference
BiConsumer<ArrayList<String>, String> accumulator = ArrayList::add;

Finally, you need a combiner that will combine the results of two ArrayList<String> into one ArrayList<String>.

The combiner is used only when you collect the results using a parallel stream.

In a sequential stream, the accumulator is sufficient to collect all results.

Your combiner will add all the elements of the second list to the first list using the addAll() method.

You can use either of the following statements to create a combiner:

// Using a lambda expression
BiConsumer<ArrayList<String>, ArrayList<String>> combiner =
        (list1, list2) -> list1.addAll(list2);

// Using a constructor reference
BiConsumer<ArrayList<String>, ArrayList<String>> combiner = ArrayList::addAll;

Put all logics together:

List<String> names = Person.persons()
                           .stream()
                           .map(Person::getName)
                           .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(names);

Related Topic