Stream Col­lect­ors are a powerful feature in the Java 8 Stream API that allow you to collect and process data ef­fi­ciently. Here we’ll explain their structure and how the Java collect() method can be used.

How can Java collect() be used?

A Stream Collector can be used to create a list, set, or map from a stream. A stream is a sequence of elements that are processed one after another. The Collector interface provides a set of reduction op­er­a­tions for data in a stream pipeline. These are terminal op­er­a­tions that collect and merge the results of in­ter­me­di­ate steps.

Col­lect­ors can be used to filter or sort objects from a stream. Ag­greg­a­tion is also possible, such as summing numbers, combining strings, or counting elements. In addition, Col­lect­ors have functions that can transform the contents of a stream into a specific structure. You can transform a list into a map, for example. Groupings help cat­egor­ise elements with certain prop­er­ties or con­di­tions. Most im­port­antly, Stream Col­lect­ors have the advantage that they can process data at the same time using multiple threads. This enables op­er­a­tions to be performed much faster and more ef­fi­ciently, es­pe­cially with large amounts of data.

What is the syntax for Java collect()?

The method accepts a Collector that describes how the elements of the stream should be collected and ag­greg­ated as an argument. A Collector is an interface that provides various methods to aggregate stream elements into a specific form, for example, into a list, a set or a map.

There are two variants of the Java Stream collect() method:

  1. <R> R collect(Supplier<R> supplier, BiCon­sumer<R, ? super T> ac­cu­mu­lat­or,BiCon­sumer<R, R> combiner)
  2. <R, A> R collect(Collector<? super T, A, R> collector)

The first variant has three functions as arguments:

  • supplier: creates a container that will be used for in­ter­me­di­ate results
  • ac­cu­mu­lat­or: cal­cu­lates the final result
  • combiner: combines the results of parallel stream op­er­a­tions

These pre­defined Col­lect­ors are already included in the standard library and can easily be imported and used.

The second variant accepts a Collector as an argument and returns a result.

  • R: the type of result
  • T: the type of elements in the stream
  • A: the type of ac­cu­mu­lat­or that stores the in­ter­me­di­ate state of the collector operation
  • collector: executes the reduction operation.

By using this variant, de­velopers can create cus­tom­ised Col­lect­ors that are spe­cific­ally tailored to their re­quire­ments and provide greater flex­ib­il­ity and control over the reduction process.

What are practical examples for using Java collect()?

Below we il­lus­trate various functions of the Stream.collect() method. You should already be familiar with the basic Java operators before jumping into the col­lec­tion framework.

Con­cat­en­ate a list of strings

With Java Collect(), we can con­cat­en­ate a list of strings to get a new string:

List<String> letters = List.of("a", "b", "c", "d", "e");
// without combiner function
StringBuilder result = letters.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append(",").append(b));
System.out.println(result.toString());
// with combiner function
StringBuilder result1 = letters.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append(",").append(b));
System.out.println(result1.toString());
Java

The output is:

abcde
a, b, c, d, e
Java

In the first cal­cu­la­tion, only one String­Build­er instance was used and there was no combiner function. This is why the result is abcde.

In the second output, the combiner function merged the String­Build­er instances and separated them with a comma.

Collect elements in a list with toList()

We can use the filter() function to select certain elements of a list and then use toList() to store them in a new list.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
List<Integer> oddNumbers = numbers.stream().filter(x -> x % 2 != 0).collect(Collectors.toList());
System.out.println(oddNumbers);
Java

In the new list, there are only odd numbers:

[1, 3, 5, 7]
Java

Collect elements in a set with toSet()

Similarly, we can select elements and create a new set from them. The elements in a set don’t have to be arranged in a specific order.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Set<Integer> evenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println(evenNumbers);
Java

This displays the output:

[2, 4, 6]
Java

Collect elements in a map with toMap()

A map can be used together with Java collect() to assign a value to each key.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Map<Integer, String> mapEvenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0)
    .collect(Collectors.toMap(Function.identity(), x -> String.valueOf(x)));
System.out.println(mapEvenNumbers);
Java

In the output, we can see that each even number in the input has been assigned a value that is identical to it:

{2=2, 4=4, 6=6}
Java

Combine elements in a string with joining()

The joining() method combines elements in a stream in the order in which they appear and uses a separator to separate the elements. The separator is passed as an argument to joining(). If no separator is specified, joining() uses the empty string "".

jshell> String result1 = Stream.of("a", "b", "c").collect(Collectors.joining());
jshell> String result2 = Stream.of("a", "b", "c").collect(Collectors.joining(",", "{", "}"));
Java

The results are:

result1 ==> "abc"
result2 ==> "{a,b,c}"
Java

2031f050ecb6acd05c8d5a15242b684b

b0935f2d0a8a22847f34c718bc1097e2

Go to Main Menu