Vavr Collections and Java Stream API Collectors

Vavr is now a must-have for every modern Java 8+ project. It encourages writing code in a functional manner by providing a new persistent Collections API along with a set of new Functional Interfaces and monadic tools like Option, Try, Either, etc.

You can read more about the whole library here.

Vavr’s Persistent Collections API

To provide useable immutable data structures, the whole Collections API needed to be redesigned from scratch.

The standard java.util.Collection interface contains methods that discourage immutability such as:

boolean add(E e);
boolean remove(Object o);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);

One might think that the problem is that those methods allow modifications of the particular collection instance, but this is not entirely true – with immutable data structures, each mutating operation needs to derive a new collection from the existing one. Simply put, each of those methods should be able to return a new instance of the collection.

Here, the whole collections hierarchy is restricted to returning boolean or void from mutating methods – which makes them suitable only for mutable implementations.

Of course, immutable implementations of java.util.Collection exist, but above-mentioned methods are simply forbidden. That’s how it looks like in the

* Guaranteed to throw an exception and leave the list unmodified.
* @throws UnsupportedOperationException always
* @deprecated Unsupported operation.
public final void add(int index, E element) {
throw new UnsupportedOperationException();

And this is far from perfect – even the simplest add() operation becomes a ceremony:

ImmutableList<Integer> original = ImmutableList.of(1);
List<Integer> modified = new ImmutableList.Builder<Integer>()

A major redesign made it possible to interact with immutable collections more naturally and to add some new features:

import io.vavr.collection.List;
// ...
List<Integer> original = List.of(1);
List<Integer> modified = original.append(2);
modified.dropWhile(i -> i < 42);
modified.foldLeft(0 , Integer::sum)

Collecting Vavr’s Collections

One of the key features of the Java Stream API was the collect() API that made it possible to take elements from Stream and apply the provided strategy to them – in most cases that would be simply placing all elements in some collection.

Vavr’s collections have a method that provides the similar(but limited) functionality but it’s not being used often because almost all operations that were available only using Stream API, are available on the collection level in Vavr.

But… one of the method signatures of Vavr’s collect() is especially intriguing:

<R, A> R collect(<? super T, A, R> collector)

As you can see, Vavr’s collections are fully compatible with Stream API Collectors and we can use our favourite Collectors easily:


That might not be super useful for everyday use-cases because the most common operations are accessible without using Collectors but it’s comforting to know that Vavr’s functionality is a superset of Stream API’s (at least in terms of collect() semantics)

Collecting Everything

The interesting realization happens when we decide to investigate the type hierarchy in Vavr:


We can notice here that the Value resides on top collections hierarchy and this is where the mentioned above collect() method is defined.

If we look closer, it’s clear that classes like Option, Try, Either, Future, Lazy also implement the Value interface. The reasoning behind this is that they are all essentially containers for values – containers that can hold max up to one element.  

This makes them compatible with Stream API Collectors, as well:

Try.of(() -> URI.create(""))


The redesign of the Collections API allowed the introduction of cool new methods, as well as achieving full interoperability with Java Stream API Collectors – which can also be applied to Vavr’s functional control structures like Option, Try, Either, Future, or Lazy.

The examples above use:

  • Pingback: Java Weekly, Issue 191 | Baeldung()

  • OK I understand the functional hype and the need to clean the good old Java API but what about the performances if you favorise immutable lists?

    • Grzegorz Piwowarek

      There is some trade-off indeed – nothing is for free. You trade performance for thread-safety and overall benefits of dealing with immutable values. The biggest problem is the increased creation of objects – when you create a mutable list with N elements, you create only 1 list and if you create a persistent list, you essentially create N nested lists.
      If you want to see exact numbers, check the bottom of the README here
      and that video(42:04):

      Everything depends on the context but I personally treat mutability as a form of optimization – and I try to avoid doing that prematurely. In projects I’ve been working on, the performance impact was neglectible.

      • Maybe because I have been primarily a C/C++ developer I see immutability programming as an energy excessive consumption as the fact to prefer create a container rather than optimize a little ounce of code. Energy should be preserved as the nature 🙂 Greencoding in a sense. Less philosophical thoughts. I agree with you about thread safety. However it has been demonstrated that the number of use cases requiring parallel streams is low.

        Finally and to conclude I think that is a really good initiative in the sense of a API clearing rather than immutability.