BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Eclipse Collections 11.0.0 Features New APIs and Functionality

Eclipse Collections 11.0.0 Features New APIs and Functionality

This item in japanese

Lire ce contenu en français

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.

About the Author

Rate this Article

Adoption
Style

BT