Eclipse Collections 11.0.0 Introduces New APIs and Features

0

The release of Eclipse Collections 11.0.0, an open source collections library for Java compatible with Java collection types, provides new methods and features for improved performance. the ClassComparer class was introduced to compare the methods of two classes and show the similarities and differences.

Originally named GS Collections by its creator Don Raab, the frame was donated to the Eclipse Foundation in December 2015 and renamed 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 managers, are built and tested with JDK 8, 11, 17 and 18 in early access.

Various methods, such as selectWithIndex and rejectWithIndexhave been added to filter elements based on the index and value of a OrderedIterable Where 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 can be converted to a MutableList either with the toSortedList with a Comparator as an argument or the toSortedListBy by providing 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 parts 1 and 2 of her blog series, have been added to Set. The first method, unioncombines the elements of 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 selects the elements present in the two 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, differencekeeps 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 keeps elements that are unique in one of 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 the method returns true if all the 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 strictest version, isProperSubsetOfReturn true if all the 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 returns all ordered pairs where the first element comes from the pair in 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 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, provide performance advantages over the functional equivalent anySatisfy and noneSatisfy at 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 contains the isEqual and isSame default methods to compare values:

Twin equalTwin = Tuples.twin("James", "James");
assertTrue(equalTwin.isEqual());

Triplet equalTriplet = Tuples.triplet("James", "James", "James");
assertTrue(equalTriplet.isEqual());

Twin sameTwin = Tuples.twin("James", new String("James"));
assertFalse(sameTwin.isSame());

Triplet sameTriplet = 
    Tuples.triplet("James", "James", new String("James"));
assertFalse(sameTriplet.isSame());

In addition to comparing values, it is now also possible to convert Pair and Triple several List types:

Twin twin = Tuples.twin("James", "Mike");
MutableList pairMutableList = Tuples.pairToList(twin);
FixedSizeList pairFixedSizeList = Tuples.pairToFixedSizeList(twin);
ImmutableList pairImmutableList = Tuples.pairToImmutableList(twin);

Triplet triplet = Tuples.triplet("James", "Mike", "Patrick");
MutableList tripletMutableList = Tuples.tripleToList(triplet);
FixedSizeList tripletFixedSizeList = 
    Tuples.tripleToFixedSizeList(triplet);
ImmutableList tripletImmutableList = 
    Tuples.tripleToImmutableList(triplet);

A Bag is an unordered collection that may contain duplicates. It is mainly used to determine and remove the number of occurrences per element. This release provides several new methods for Bag which are demonstrated on the basis of the following example:

Bag names = Bags.mutable.with("James", "James", "Mike", "Patrick");

It is now possible to check if the Bag contains an element:

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 check if there are elements 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 bag = exampleList.stream()
    .collect(Collectors2.toImmutableSortedBagBy(Math::negateExact));

Comparator comparator = Functions.toIntComparator(Math::negateExact);
ImmutableSortedMap 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 list = List.of(1, 2, 3);
Comparator c =
	Functions.toIntComparator(Math::negateExact);
MutableSortedMap 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 is possible to create immutable maps:

ImmutableMap immutableMap = Maps.immutable.empty();
ImmutableMap resultingImmutableMap = 
    immutableMap.newWithMap(UnifiedMap.newMapWith(
		Tuples.pair("Simon", 1),
		Tuples.pair("Mike", 2)));

ImmutableMapIterable immutableMapIterable = 
    Maps.immutable.empty();
ImmutableMapIterable resultingImmutableMapIterable = 
    immutableMap.newWithMapIterable(UnifiedMap.newMapWith(
		Tuples.pair("Simon", 1),
		Tuples.pair("Mike", 2)));

the withMapIterable and putAllMapIterable methods have been added to MutableMap for consistency.

the eclipse-collections-testutils the module now contains ClassComparer to compare classes. This results in a sort of Venn diagram displaying common methods and class-specific methods, which can optionally be displayed with an experimental Swing UI. Comparing IntIterable.class and RichIterable.class displays the following results, which only contain methods beginning 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]
…

Alternately, 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 conversion methods, such as toList and toSortedSet, have been available for a while, but their immutable counterparts were not available. It was however possible to convert to immutable using the toImmutable method, but sometimes required two steps: .toList().toImmutable(). To improve consistency with mutable peers, toImmutableList, toImmutableSet and toImmutableBag have been added to RichIterable and other methods may follow in the future.

The full list of changes is available on the GitHub release page.

Share.

Comments are closed.