How to collect Immutable Collection in Java

By | April 17, 2015

To begin with this story, let’s first have a look at how to creat a List from Stream in Java

List<String> sublist = list
  .stream()
  .filter(...)
  .collect(Collectors.toList());

This works perfectly fine but what if we want the list to be immutable? We could do this

List<String> immutableSubList = Collections.unmodifiableList(sublist);

or if we would like to use Guava ImmutableList, we could do

ImmutableList<String> immutableSubList = ImmutableList.copyOf(sublist);

However this is a bit awkward to use since the list will be copied one more time. If we want to do this in a lot of places throughout the code base, it is not fluid. Instead, what we want is

ImmutableList<String> sublist = list
  .stream()
  .filter(...)
  .collect(ImmutableCollectors.toList());

This post will discuss how to create the Collector of ImmutableList.

Collector

To create a Collector, we will use the static method of.

public static<t, A, R> Collector<T, A, R> of(
  Supplier<A> supplier,
  BiConsumer<A, T> accumulator,
  BinaryOperator<A> combiner,
  Function<A, R> finisher,
  Characteristics... characteristics);

Here’s a short explaination of the parameters

  • The supplier returns the resulting object that will be populated by the collector.
  • The accumulator adds an element from the stream into the list created by the supplier.
  • The combiner combines two list instances into one. This function is called by the collector when the stream is in parallel mode.
  • The characteristics provides hints to the Collector how it can optimize reduction implementations. We leave it blank here.

Collect ImmutableList

  public static <T> Collector<T, ?, ImmutableList<T>> toList() {
    return Collector.of(
        ImmutableList.Builder::new,
        ImmutableList.Builder::add,
        (left, right) -> left.addAll(right.build()),
        (Function<ImmutableList.Builder<T>, ImmutableList<T>>)
            ImmutableList.Builder::build);
  }

The implementation uses the Collector.of method. I use

  • ImmutableList.Builder::new to create a supplier.
  • ImmutableList.Builder::add to add elements from the stream into the builder.
  • The combiner function combines the results of two different supplier instances (created when the stream is in parallel mode). So if we have two builders, we can combine them by calling the addAll method, then return the left builder.
  • ImmutableList.Builder::build to build the ImmutableList.

Collect ImmutableSet

Similarly, we can create immutable set

  public static <T> Collector<T, ?, ImmutableSet<T>> toSet() {
    return Collector.of(
        ImmutableSet.Builder::new, 
        ImmutableSet.Builder::add, 
        (left, right) -> left.addAll(right.build()), 
        (Function<ImmutableSet.Builder<T>, ImmutableSet<T>>)
            ImmutableSet.Builder::build,
        Collector.Characteristics.UNORDERED);
  }

Here, I use Collector.Characteristics.UNORDERED to indicate that the collection operation does not commit to preserving the encounter order of input elements.

Test cases

  @Test
  public void test_toList() {
    List<String> list = Lists.newArrayList("a", "b");
    ImmutableList<String> sublist = list
        .stream()
        .filter(s -> s.charAt(0) == 'a')
        .collect(ImmutableCollectors.toList());
    assertTrue(Iterables.getOnlyElement(sublist).equals("a"));
  }
  
  @Test
  public void test_toSet() {
    List<String> list = Lists.newArrayList("a", "b");
    ImmutableSet<String> subset = list
        .stream()
        .filter(s -> s.charAt(0) == 'a')
        .collect(ImmutableCollectors.toSet());
    assertTrue(Iterables.getOnlyElement(subset).equals("a"));
  }

Conclusion

The full code can be found at ImmutableCollectors in the library of pengyifan-commons.

<repositories>
    <repository>
        <id>oss-sonatype</id>
        <name>oss-sonatype</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>
...
<dependency>
  <groupId>com.pengyifan</groupId>
  <artifactId>pengyifan-commons</artifactId>
  <version>0.1.0-SNAPSHOT</version>
</dependency>

Leave a Reply

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