Nine Features in Eclipse Collections 9.0

CountBy, DistinctBy, Cartesian Product for primitive collections… and more.

In my previous blog post, I described the upcoming release of Eclipse Collections 9.0.

In this post, I will briefly highlight nine features with examples that will be included in the Eclipse Collections 9.0 release. Each example shows what you could do in Eclipse Collections 7.x — 8.x and how it was improved in Eclipse Collections 9.0.

1. CountBy

// Eclipse Collections 7.x - 8.x
MutableBag<String> countsOld =
this.company.getCustomers()
.asLazy().collect(Customer::getCity).toBag();
// Eclipse Collections 9.x
MutableBag<String> countsNew =
this.company.getCustomers()
.countBy(Customer::getCity);

It should be easier for developers to both discover and read countBy versus the previous alternative.

2. DistinctBy

// Eclipse Collections 7.x - 8.x
MutableList<Customer> distinctOld =
this.company.getCustomers()
.distinct(
HashingStrategies.fromFunction(Customer::getCity));
// Eclipse Collections 9.x
MutableList<Customer> distinctNew =
this.company.getCustomers()
.distinctBy(Customer::getCity);

DistinctBy uses distinct with a hashing strategy for its implementation.

3. Primitive Collection Factories work with Primitive Java Streams

// Eclipse Collections 7.x - 8.x
MutableIntList listOld =
IntStream.rangeClosed(1, 100)
.collect(
IntLists.mutable::empty,
MutableIntList::add,
MutableIntList::withAll);
// Eclipse Collections 9.x
MutableIntList listNew =
IntLists.mutable.withAll(
IntStream.rangeClosed(1, 100));

There are also immutable factories that take IntStream, LongStream and DoubleStream. Both mutable and immutable factories that accept primitive Java Streams are available across primitive Lists, Sets, Bags, and Stacks for int, long and double. Symmetric Sympathy strikes again.

4. Factory classes can now create adapters

// Eclipse Collections 7.x - 8.x
MutableList<Object> listAdapter =
ListAdapter.adapt(new ArrayList<>());
MutableSet<Object> setAdapter =
SetAdapter.adapt(new ConcurrentSkipListSet<>());
MutableMap<Object, Object> mapAdapter =
MapAdapter.adapt(new LinkedHashMap<>());
// Eclipse Collections 9.x
MutableList<Object> listAdapter =
Lists.adapt(new ArrayList<>());
MutableSet<Object> setAdapter =
Sets.adapt(new ConcurrentSkipListSet<>());
MutableMap<Object, Object> mapAdapter =
Maps.adapt(new LinkedHashMap<>());

Many developers do not realize there are adapter classes for existing JDK types in Eclipse Collections. Adding this short-cut on the factory classes should make it easier to discover.

5. Streams available directly on Immutable Collections

// Eclipse Collections 7.x - 8.x
boolean result =
Lists.immutable.with(1, 2, 3)
.castToList()
.stream()
.anyMatch(i -> i % 2 == 0);
// Eclipse Collections 9.x
boolean result =
Lists.immutable.with(1, 2, 3)
.stream()
.anyMatch(i -> i % 2 == 0);

This was a requested feature. It is not obvious that you can call castToList and then call stream on an ImmutableCollection. In this previous post I described the design decision to not have ImmutableCollection extend Collection. This is why it was necessary to add stream explicitly.

6. FlatCollect on primitive lazy iterables

// Eclipse Collections 8.x
IntList listOne = IntLists.mutable.with(1, 2, 3);
IntList listTwo = IntLists.mutable.with(4, 5, 6);
MutableList<IntIntPair> pairs = listOne
.flatCollect(i ->
listTwo.collect(j ->
PrimitiveTuples.pair(i, j)),
Lists.mutable.empty());
// Eclipse Collections 9.x
IntList listOne = IntLists.mutable.with(1, 2, 3);
IntList listTwo = IntLists.mutable.with(4, 5, 6);
LazyIterable<IntIntPair> pairs =
listOne.asLazy()
.flatCollect(i -> listTwo.asLazy()
.collect(j -> PrimitiveTuples.pair(i, j)));

Previously, you could only flatCollect directly into a mutable primitive container. This method was an important building block for primitive cartesian product so it could be implemented lazily.

7. Streams available for values on all Object Valued Maps

// Eclipse Collections 7.x - 8.x - Object Maps
boolean result =
Maps.mutable.with(1, 1, 2, 2, 3, 3)
.values()
.stream()
.anyMatch(i -> i % 2 == 0);
// Eclipse Collections 9.x - Object Maps
boolean result =
Maps.mutable.with(1, 1, 2, 2, 3, 3)
.stream()
.anyMatch(i -> i % 2 == 0);
// Eclipse Collections 7.x - 8.x - PrimitiveObject Maps
boolean primitiveResult =
IntObjectMaps.mutable.<Integer>empty()
.withKeyValue(1, 1)
.withKeyValue(2, 2)
.withKeyValue(3, 3)
.values()
.stream()
.anyMatch(i -> i % 2 == 0);
// Eclipse Collections 9.x - PrimitiveObject Maps
boolean primitiveResult2 =
IntObjectMaps.mutable.<Integer>empty()
.withKeyValue(1, 1)
.withKeyValue(2, 2)
.withKeyValue(3, 3)
.stream()
.anyMatch(i -> i % 2 == 0);

All object valued maps implement RichIterable<V>. This was a conscious design decision made in the early days of Eclipse Collections. Unfortunately, the stream method is defined on Collection which Maps in Eclipse Collections do not extend. So the stream method had to be added explicitly.

8. AverageIfEmpty and MedianIfEmpty on primitive iterables

// Eclipse Collections 7.x - 8.x
double average = IntLists.mutable.empty().average();
// throws java.lang.ArithmeticException
double median = IntLists.mutable.empty().median();
// throws java.lang.ArithmeticException
// Eclipse Collections 9.x
double average = IntLists.mutable.empty().averageIfEmpty(0.0);
double median = IntLists.mutable.empty().medianIfEmpty(0.0);

Eclipse Collections had minIfEmpty and maxIfEmpty on primitive iterables but did not have the equivalent for average and median, which both throw on empty.

9. Primitive Sets now have Cartesian Product

// Eclipse Collections 7.x - 8.x
Set<Integer> a = Sets.mutable.with(1, 2, 3);
Set<Integer> b = Sets.mutable.with(4, 5, 6);
LazyIterable<Pair<Integer, Integer>> pairs =
Sets.cartesianProduct(a, b);
// Eclipse Collections 9.x
IntSet a = IntSets.mutable.with(1, 2, 3);
IntSet b = IntSets.mutable.with(4, 5, 6);
LazyIterable<IntIntPair> pairs =
IntSets.cartesianProduct(a, b);

Eventually I hope we will have all of the methods available on Sets today on the equivalent primitive Sets classes. There are methods like difference, symmetricDifference, powerSet, etc. on the Sets class today. Cartesian Product was a good first step to providing better symmetry for primitive collections.

…and there is more. See the detailed release notes here.