LazyIterable will get back to you later
Learn about a lesser known lazy Iterable type in Eclipse Collections.
Old, Rich, Lazy, and Inexhaustible
The open source Eclipse Collections Java library has a type named LazyIterable
that provides a lazy alternative to methods that would otherwise return collection types. LazyIterable
is a teenager, has a very feature-rich API, is lazy when possible, and inexhaustible. This means an instance of LazyIterable
can be used over and over again.
The Old
LazyIterable
has been available publicly since GS Collections 1.0 was released in January 2012. This means LazyIterable
was developed and used in internal production applications in Goldman Sachs some time before 2012. LazyIterable
is a teenager, which in dog years means it has been around the block a few times.
Eclipse Collections started out as an eager-only library. As use cases rolled in from clients over the years, fused methods like collectIf
were added to reduce multiple iterations for common pattern combinations like select
+ collect
, a.k.a. filter
+ map
.
LazyIterable
was added to the library so the same useful and familiar methods on RichIterable
could be executed both eagerly and lazily.
The Rich
LazyIterable
sits below RichIterable
in the Eclipse Collections interface hierarchy.
All MutableCollection
and ImmutableCollection
types can return a LazyIterable
by calling the method named asLazy
.
The primitive collection hierarchy also has LazyIterable
equivalents for each of the eight Java primitive iterable types.
These UML class hierarchies show only the breadth of laziness available in the Eclipse Collection library. They do not show the depth. The following Venn Diagram shows the method counts of LazyIterable
and Java Stream
, along with Collectors
and Collectors2
.
The following table shows the unique and common method names (and counts) between LazyIterable
and Java Stream
.
The following table shows the unique and common method names (and counts) between LazyIterable
and Collectors2
. Collectors2
is a utility class in Eclipse Collections that supplies Collector
instances that work with the Java Stream
collect
method.
The following table shows the unique and common method names (and counts) between LazyIterable
and Java Collectors
.
The intersections between Stream
, Collectors
, and Collectors2
are less interesting. The only method Stream
and Collectors
have in common is toList
. The only methods that Collectors
and Collectors2
have in common are toList
, toSet
, and toMap
. The only methods Stream
and Collectors2
have in common are collect
and toList
. The difference between the method named collect
in Eclipse Collections and collect
in Java Stream is stark.
To further understand what makes LazyIterable
a feature-rich interface, I recommend reading the following blog.
The Lazy and Inexhaustible
The difference between an Iterable
and a Stream
in Java may be confusing to some. LazyIterable
is an Iterable
. The expectation of an Iterable
is that it is a supplier of an Iterator
. While Stream
has an iterator
method, it is not an Iterable
. Stream
can only supply this and any other terminal methods safely, once. This has confused plenty of developers over the years.
The following code shows how iterator
may be called repeatedly on a LazyIterable
, and only callable once on a Stream
.
@Test
public void streamAndLazyIterableIteratorComparison()
{
MutableList<Integer> integers = Lists.mutable.of(1, 2, 3, 4, 5);
Stream<Integer> stream = integers.stream();
// Terminal methods may be only used once with a Stream
stream.iterator().forEachRemaining(System.out::print);
Assertions.assertThrows(IllegalStateException.class,
() -> stream.iterator().forEachRemaining(System.out::print));
Assertions.assertThrows(IllegalStateException.class,
() -> stream.iterator().forEachRemaining(System.out::print));
// Prints : 12345
System.out.println();
// Methods on LazyIterable can be used as many times as needed
LazyIterable<Integer> lazyIterable = integers.asLazy();
lazyIterable.iterator().forEachRemaining(System.out::print);
lazyIterable.iterator().forEachRemaining(System.out::print);
lazyIterable.iterator().forEachRemaining(System.out::print);
// Prints : 123451234512345
}
This important difference makes it possible for a LazyIterable
to be used safely as both a parameter to and as a return value from a method. Stream
is unsafe to use as both a parameter and return value, because it is possible that the Stream
has already been exhausted.
There are potential performance implications of passing around LazyIterable
, as any processing that is stacked up lazily has to be repeated each time. If performance is an issue, then store the results of processing intermediate results from a LazyIterable
into a Collection
first.
For developers like myself who are lazy and would get quickly exhausted trying to learn so many methods, there are better ways to group methods in a Class than just alphabetically.
Here are all of the methods in LazyIterable
grouped by prefix.
Here all all the methods in LazyIterable
grouped by suffix.
Rich interfaces like LazyIterable
and Stream
require more advanced grouping features in the Java language to help developers more quickly find what they are looking for. This is why I am an advocate for Method Categories being added to the Java language.
To see other ways of breaking Java Stream
into smaller chunks, check out the following blog.
Further Reading
I wrote a blog almost seven years ago that shows what it means for LazyIterable
to be both lazy and inexhaustible.
I also explain in the following blog how Eclipse Collections evolved from eager, to fused, to lazy over the years.
If the differences between eager and lazy are unclear, the following blog might help. I recommend learning and using eager iteration patterns before learning and using lazy iteration patterns. Eager is simpler to use and understand. Lazy is sometimes, but not always, a performance optimization. Standard rules of premature performance optimization apply. Go simple unless you can prove you will go faster.
Thank you!
I hope you find this blog useful. The differences between LazyIterable
and Stream
are subtle but important. I believe these differences are not not well known. As often the case with Eclipse Collections, there is not an either/or question when it comes to using LazyIterable
or Java Stream
. Both can be used for good effect with Eclipse Collections types. Use whatever works best for the problems you are trying to solve.
Thanks for reading!
I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions.