From Eager to Fused to Lazy

Donald Raab
Javarevisited
Published in
4 min readJun 25, 2020

An evolution of iteration methods in Eclipse Collections

Three ways of accomplishing the same goal — eager, fused and lazy

In the beginning there was Eager

When the first iteration methods were implemented in Eclipse Collections they were all eager. Eager is the default behavior on the collection interfaces and implementations in Eclipse Collections.

When using eager methods with multiple operations in a fluent style, you may create intermediate collections.

Both select and collect are eager methods

The call to select above will create a MutableList<Integer>. The call to collect then creates a MutableList<String>. The following code shows explicitly the return types of the calls to select and collect.

With Eager, each call forces execution and returns a List

The imperative code for select and collect would look as follows.

First we select, and then we collect

Optimizing with a Fused method

When we started replacing imperative for loops with higher level iteration patterns we occasionally saw for loops that were doing multiple things. These for loops could be broken down into individual steps like select and collect, but it was then clear that there was more temporary garbage being created. This is when we introduced one of the first fused methods named collectIf. The method collectIf is a shorter name for selectAndCollect.

The fused method collectIf includes both the Predicate and Function required to filter and transform the list in one pass.

Take all evens and transform them using toString

This is what an imperative for loop would look like if it was doing a collectIf operation.

Classic imperative filter and transform of a List

The fused operation collectIf is as close to the imperative for loop as you can get only without having the list creation, the for loop, the if statement and the call to add on the collection.

Time to be Lazy

We added the lazy API to Eclipse Collections after the eager and fused API. The lazy API in Eclipse Collections is available via a call to asLazy. The lazy API will defer execution until a terminal operation is called (like forEach or toList). The lazy API in Eclipse Collections returns an Iterable type named LazyIterable. Unlike Stream which was added in Java 8, LazyIterable can be reused. The following blog has more about LazyIterable and how it is inexhaustible.

The benefit of the lazy API is that intermediate collections are not created. The calls to asLazy, select, and collect all create LazyIterable instances that do nothing. The final call to toList forces execution to happen and creates the target list.

Lazy works well in a fluent style

The following code shows the result of each operation.

The final call to toList forces execution to happen and the result list is created

The following imperative code shows how two Iterables can be composed together and then iterated using a for loop to achieve the same result.

Create a SelectIterable, compose a CollectIerable on the SelectIterable, then iterate adding to a List

Recommendations

Sometimes it is better to use eager, fused or lazy. Here are the rules I recommend with Eclipse Collections.

  1. Use eager methods for single operations, or if you are working with small collections.
  2. Use a fused method if it matches exactly to the scenario you have like collectIf.
  3. Use lazy if you have more than one operation that would otherwise cause more temporary collections to be created.

I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse Foundation. Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.

--

--

Donald Raab
Javarevisited

Java Champion. Creator of the Eclipse Collections OSS Java library (https://github.com/eclipse/eclipse-collections). Inspired by Smalltalk. Opinions are my own.