1. Introduction
In this quick article, we’re going to have a look at various ways of collecting Java Streams to immutable Collections – which require a special approach because standard Collectors work only with mutable data structures.
2. Maven Dependency
We are going to make use of the Google’s Guava library to drive some of our examples:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>22.0</version> </dependency>
We can get the latest version of this dependency from here.
3. Using Java’s collectingAndThen()
The collectingAndThen() method from Java’s Collectors class accepts a Collector, and a finisher Function that is applied to the result returned from the Collector:
@Test public void whenUsingCollectingToImmutableList_thenSuccess() { List<String> givenList = Arrays.asList("a", "b", "c"); List<String> result = givenList.stream() .collect(collectingAndThen(toList(), ImmutableList::copyOf)); System.out.println(result.getClass()); }
Since we can’t use the toCollection() Collector directly, we need to collect elements to a temporary list and then construct an immutable list from it.
In this example, we are converting a Stream to a List using the toList() collector and then creating an ImmutableList. The ImmutableList is a part of the Guava library. If we log the output to the console, we’ll get the class of the underlying
If we log the output to the console, we’ll get the class of the underlying List implementation:
class com.google.common.collect.RegularImmutableList
4. Using Guava’s Collectors
Starting with Guava 21, with every immutable class comes an accompanying Collector that is as easy to use as standard Collectors:
@Test public void whenCollectToImmutableList_thenSuccess() { List<Integer> list = IntStream.range(0, 9) .boxed() .collect(ImmutableList.toImmutableList()); }
The resulting instance is the RegularImmutableList:
class com.google.common.collect.RegularImmutableList
5. Building a Custom Collector
Now, let’s move one step further and implement our custom Collector. To achieve this goal, we are going to use the static Collector.of() method:
public static <T> Collector<T, List<T>, List<T>> toImmutableList() { return Collector.of(ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, Collections::unmodifiableList); }
To learn more about implementing custom Collectors, please have a look at Section 4 of this article. And that’s it. The above method is a part of our custom class
We can use it now just like any other built-in Collectors:
@Test public void whenCollectToMyImmutableListCollector_thenSuccess() { List<String> givenList = Arrays.asList("a", "b", "c", "d"); List<String> result = givenList.stream() .collect(MyImmutableListCollector.toImmutableList()); }
Finally, let’s check the output:
class java.util.Collections$UnmodifiableRandomAccessList
5.1. Making the MyImmutableListCollector Generic
Our implementation has one limitation – it always returns an immutable instance backed by an ArrayList. However, with slight improvement, we can make this collector return a user-specified type:
public static <T, A extends List<T>> Collector<T, A, List<T>> toImmutableList( Supplier<A> supplier) { return Collector.of( supplier, List::add, (left, right) -> { left.addAll(right); return left; }, Collections::unmodifiableList); }
Now instead of determining the supplier in the method implementation, we are requesting the supplier from the user:
@Test public void whenPassingSupplier_thenSuccess() { List<String> givenList = Arrays.asList("a", "b", "c", "d"); List<String> result = givenList.stream() .collect(MyImmutableListCollector.toImmutableList(LinkedList::new)); }
Please note that we are now using the LinkedList instead of ArrayList. Let’s run this and see the results:
class java.util.Collections$UnmodifiableList
This time, we got UnmodifiableList instead of UnmodifiableRandomAccessList.
6. Conclusion
In this short article, we have seen various ways to collect a Stream into an immutable Collection.
Make sure to check out the full source code of this article over on GitHub.