The release of Eclipse Collections 11.0.0, an open-source collections library for Java compatible with the Java collection types, provides new methods and functionalities for improved performance. The ClassComparer
class was introduced to compare the methods of two classes and display the similarities and differences.
Originally named GS Collections by its creator Don Raab, the framework was donated to the Eclipse Foundation in December 2015 and rebranded as the Eclipse Collections. Version 11.0.0 is the first release since version 10.4 in August 2020. Eclipse Collections, maintained by these committers and project leads, is built and tested against JDK 8, 11, 17 and 18 early access.
Various methods, such as selectWithIndex
and rejectWithIndex
, were added to filter elements based on the index and the value for a OrderedIterable
or ListIterable
:
var exampleList = Lists.mutable.with(1, 2, 3, 4, 5);
var selectWithIndexList =
exampleList.selectWithIndex((value, index) -> value + index < 6);
assertEquals(Lists.mutable.with(1, 2, 3), selectWithIndexList);
var rejectWithIndexList =
exampleList.rejectWithIndex((value, index) -> value + index < 6);
assertEquals(Lists.mutable.with(4,5), rejectWithIndexList);
Primitive iterables may be converted to a MutableList
with either the toSortedList
with a Comparator
as argument or the toSortedListBy
by supplying a Function
:
var exampleSet = Sets.mutable.with(1, 2, 3, 4, 5);
var sortedList = exampleSet.toSortedList((val1, val2) -> val2 - val1);
var expected = Lists.mutable.with(5, 4, 3, 2, 1);
assertEquals(expected, sortedList);
var sortedListBy = exampleSet.toSortedListBy(Math::negateExact);
assertEquals(expected, sortedListBy);
Various methods, as detailed by Sirisha Pratha in part 1 and part 2 of her blog series, were added to Set
. The first method, union
, combines the elements from two sets:
var set1 = Sets.mutable.with(1, 2, 3);
var set2 = Sets.mutable.with(3, 4, 5);
var expectedSet = Sets.mutable.with(1, 2, 3, 4, 5);
assertEquals(expectedSet, set1.union(set2));
The intersect
method selects the elements present in both sets:
var set1 = Sets.mutable.with(1, 2, 3);
var set2 = Sets.mutable.with(3, 4, 5);
var expectedSet = Sets.mutable.with(3);
assertEquals(expectedSet, set1.intersect(set2));
Another new method, difference
, keeps the unique elements of the first set:
var set1 = Sets.mutable.with(1, 2, 3);
var set2 = Sets.mutable.with(3, 4, 5);
var expectedSet = Sets.mutable.with(1, 2);
assertEquals(expectedSet, set1.difference(set2));
The symmetricDifference
method keeps the elements that are unique in one of the two sets:
var set1 = Sets.mutable.with(1, 2, 3);
var set2 = Sets.mutable.with(3, 4, 5);
var expectedSet = Sets.mutable.with(1, 2, 4, 5);
assertEquals(expectedSet, set1.symmetricDifference(set2));
The isSubsetOf
method returns true
if all elements of the first set are present in the second set:
var set1 = Sets.mutable.with(1, 2);
var set2 = Sets.mutable.with(1, 2, 3);
assertTrue(set1.isSubsetOf(set2));
assertFalse(set2.isSubsetOf(set1));
The stricter version, isProperSubsetOf
, returns true
if all elements of the first set are present in the second set, but the sets are not equal:
var set1 = Sets.mutable.with(1, 2);
var set2 = Sets.mutable.with(1, 2, 3);
assertTrue(set1.isProperSubsetOf(set2));
var set3 = Sets.mutable.with(1, 2);
assertFalse(set1.isProperSubsetOf(set3));
The cartesianProduct
method returns all ordered pairs where the first element comes from the pair of the first set and the second element comes from the second set:
var set1 = IntSets.mutable.with(1, 2);
var set2 = IntSets.mutable.with(3, 4);
MutableSet<IntIntPair> expected = Sets.mutable.with(
PrimitiveTuples.pair(1, 3),
PrimitiveTuples.pair(1, 4),
PrimitiveTuples.pair(2, 4),
PrimitiveTuples.pair(2, 3));
assertEquals(expected, set1.cartesianProduct(set2).toSet());
Newly introduced methods, containsAny
and containsNone
for primitive collections, offer performance benefits to the functional equivalent anySatisfy
and noneSatisfy
at a memory cost:
ImmutableIntList list = IntLists.immutable.of(1, 2, 3, 4, 5);
assertTrue(list.containsAny(3, 6));
assertTrue(list.containsAny(new IntArrayList(3, 6)));
assertTrue(list.containsNone(6, 8));
assertTrue(list.containsNone(new IntArrayList(6, 8)));
Pair
and Triple
now contain the isEqual
and isSame
default methods to compare the values:
Twin<String> equalTwin = Tuples.twin("James", "James");
assertTrue(equalTwin.isEqual());
Triplet<String> equalTriplet = Tuples.triplet("James", "James", "James");
assertTrue(equalTriplet.isEqual());
Twin<String> sameTwin = Tuples.twin("James", new String("James"));
assertFalse(sameTwin.isSame());
Triplet<String> sameTriplet =
Tuples.triplet("James", "James", new String("James"));
assertFalse(sameTriplet.isSame());
Next to comparing the values, it’s also now possible to convert Pair
and Triple
to several List
types:
Twin<String> twin = Tuples.twin("James", "Mike");
MutableList<String> pairMutableList = Tuples.pairToList(twin);
FixedSizeList<String> pairFixedSizeList = Tuples.pairToFixedSizeList(twin);
ImmutableList<String> pairImmutableList = Tuples.pairToImmutableList(twin);
Triplet<String> triplet = Tuples.triplet("James", "Mike", "Patrick");
MutableList<String> tripletMutableList = Tuples.tripleToList(triplet);
FixedSizeList<String> tripletFixedSizeList =
Tuples.tripleToFixedSizeList(triplet);
ImmutableList<String> tripletImmutableList =
Tuples.tripleToImmutableList(triplet);
A Bag
is an unordered collection that might contain duplicates. It’s mostly used to determine and remove the number of occurrences per item. This version provides several new methods for Bag
which are demonstrated based on the following example:
Bag<String> names = Bags.mutable.with("James", "James", "Mike", "Patrick");
Now it’s possible to check if the Bag
contains an item:
assertTrue(names.anySatisfyWithOccurrences((object, value) ->
object.equals("Mike")));
assertTrue(names.noneSatisfyWithOccurrences((object, value) ->
object.equals("Simon")));
Or if the bag contains a specific item with the specified number of occurrences:
assertTrue(names.anySatisfyWithOccurrences((object, value) ->
object.equals("James") && value == 2));
assertTrue(names.noneSatisfyWithOccurrences((object, value) ->
object.equals("James") && value == 1));
Or verify if there are any items with a specific number of occurrences:
assertTrue(names.anySatisfyWithOccurrences((object, value) ->
value == 2));
assertTrue(names.noneSatisfyWithOccurrences((object, value) ->
value > 3));
Collectors2
now contains the toImmutableSortedBagBy
method:
var exampleList = Lists.mutable.with(1, 2, 3, 4);
ImmutableSortedBag<Integer> bag = exampleList.stream()
.collect(Collectors2.toImmutableSortedBagBy(Math::negateExact));
Comparator<Integer> comparator = Functions.toIntComparator(Math::negateExact);
ImmutableSortedMap<Integer, Integer> immutableSortedMap = exampleList.stream()
.collect(Collectors2.toImmutableSortedMap(comparator, element -> element, element -> element * 2));
var expected = SortedMaps.mutable.with(comparator, 4, 8, 3, 6, 2, 4, 1, 2);
assertEquals(expected, immutableSortedMap);
Collectors2 also provides the toImmutableSortedMap
, toImmutableSortedMapBy
, toSortedMap
and toSortedMapBy
methods:
List<Integer> list = List.of(1, 2, 3);
Comparator<Integer> c =
Functions.toIntComparator(Math::negateExact);
MutableSortedMap<Integer, String> map =
list.stream().collect(
Collectors2.toSortedMap(c, e -> e, String::valueOf));
var expected = SortedMaps.mutable.with(c, 1, "1", 2, "2", 3, "3");
assertEquals(expected, map);
With the new newWithMap
and newWithMapIterable
functions on ImmutableMap
it’s possible to create immutable maps:
ImmutableMap<String, Integer> immutableMap = Maps.immutable.empty();
ImmutableMap<String, Integer> resultingImmutableMap =
immutableMap.newWithMap(UnifiedMap.newMapWith(
Tuples.pair("Simon", 1),
Tuples.pair("Mike", 2)));
ImmutableMapIterable<String, Integer> immutableMapIterable =
Maps.immutable.empty();
ImmutableMapIterable<String, Integer> resultingImmutableMapIterable =
immutableMap.newWithMapIterable(UnifiedMap.newMapWith(
Tuples.pair("Simon", 1),
Tuples.pair("Mike", 2)));
The withMapIterable
and putAllMapIterable
methods were added to MutableMap
for consistency.
The eclipse-collections-testutils module now contains ClassComparer
for comparing classes. This results in a kind of Venn Diagram displaying the common methods and the class-specific methods, which optionally may be displayed with an experimental Swing UI. Comparing IntIterable.class
and RichIterable.class
displays the following results, which only contain methods starting with ‘a’ for readability:
new ClassComparer().compareAndPrint(IntIterable.class, RichIterable.class);
Intersection (IntIterable, RichIterable)
----------------------------------------
a:[allSatisfy, anySatisfy, appendString, asLazy]
…
Difference (IntIterable, RichIterable)
--------------------------------------
a:[average, averageIfEmpty]
…
Difference (RichIterable, IntIterable)
--------------------------------------
a:[aggregateBy, aggregateInPlaceBy, allSatisfyWith, anySatisfyWith]
…
Alternatively, ClassComparer
offers a constructor argument to optionally compare based on method names, parameter types and return types:
new ClassComparer(true, true, true)
.compareAndPrint(IntIterable.class, RichIterable.class);
Intersection (org.eclipse.collections.api.IntIterable, org.eclipse.collections.api.RichIterable)
------------------------------------------------------------------------------------------------
a:[appendString(Appendable):void, appendString(Appendable, String):void, appendString(Appendable, String, String, String):void]
…
Difference (org.eclipse.collections.api.IntIterable, org.eclipse.collections.api.RichIterable)
----------------------------------------------------------------------------------------------
a:[allSatisfy(IntPredicate):boolean, anySatisfy(IntPredicate):boolean, asLazy():LazyIntIterable, average():double, averageIfEmpty(double):double]
…
Difference (org.eclipse.collections.api.RichIterable, org.eclipse.collections.api.IntIterable)
----------------------------------------------------------------------------------------------
a:[aggregateBy(Function, Function0, Function2):MapIterable, aggregateBy(Function, Function0, Function2, MutableMapIterable):MutableMapIterable, aggregateInPlaceBy(Function, Function0, Procedure2):MapIterable, allSatisfy(Predicate):boolean, allSatisfyWith(Predicate2, Object):boolean, anySatisfy(Predicate):boolean, anySatisfyWith(Predicate2, Object):boolean, asLazy():LazyIterable]
…
Mutable converter methods, such as toList
and toSortedSet
, have been available for quite some time, but their immutable counterparts weren’t available. It was possible, however, to convert to immutable using the toImmutable
method, but sometimes required two steps: .toList().toImmutable()
. To improve consistency with the mutable counterparts, toImmutableList
, toImmutableSet
and toImmutableBag
were added to RichIterable
and other methods might follow in the future.
The full list of changes is available on the GitHub release page.