Eclipse Collections — the features your collections need

Eclipse Collections — the features your collections need

Deny it or not data structures are important. Picking the right one will enormously increase the performance of your program/product/application. It may even get you that perfect job.

Many (mainstream) programming languages come with a collection library. That provides APIs and implementations to make it easy for end-users. These implementations are fast and built to make it easier for end-users.

Providing too many options increases the learning curve of the language, providing too less leads to cumbersome implementation by the users. So languages have to be very careful in what they provide. Java strikes a perfect balance.

Eclipse collections provide optimal and efficient implementations of collections in Java.

Eclipse collections added a few additional data structures that are not natively available in the core Java.

But the most important thing is that Eclipse-Collections provides elegant, functional, and fluent APIs that you can work with.

The top reasons why I love eclipse collections are:

  • APIs are awesome. They are Functional, Lazy, Parallel, and Eager (while optimised)
  • provides immutable and mutable collections
  • provides highly-optimised and memory-efficient implementation

Set things up

If you are using a Maven project include the eclipse collection dependencies like below:

<dependency
    <groupId>org.eclipse.collections</groupId>
    <artifactId>eclipse-collections</artifactId>
    <version>10.0.0</version>
</dependency></span>

If you are using Gradle then import it:

compile 'org.eclipse.collections:eclipse-collections-api:10.0.0'
compile 'org.eclipse.collections:eclipse-collections:10.0.0'</span>

Ingredients

The Eclipse Collections consists of the following data structures:

  • List
  • Set
  • Map
  • BiMap
  • MultiMap
  • Bag
  • Stack
  • Pair and others

All those data structures include the following implementations:

  • Mutable
  • Immutable
  • Lazy
  • Parallel
  • Ordered
  • Sorted
  • Fixed
  • Primitive and others

Note not all the implementations for all the collections.


Code, Code, Code…

There are few excellent code katas available. They are here.

Let us start with a List. To instantiate a new mutable list we can do the following:

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);</span>

You can instantiate the list with of.

MutableList<Integer> firstTenNumbers = Lists.mutable.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);</span>

Thinking why there is of and with implementation — check out this blog post.

Retrieval

We can retrieve the elements using classic get(index) way. The Eclipse Collections provides getFirst() and getLast() method to retrieve the first and the last elements respectively.

firstTenNumbers.get(4);       // 4
firstTenNumbers.getFirst();   // 0
firstTenNumbers.getLast();    // 9</span>

In functional paradigm, take | takeWhile | drop | dropWhile APIs are explicit and makes it easy to construct functions. They take and drop certain elements from the list without mutating them. With Eclipse Collections we can do that in Java.

drop and take

The functions drop and take each take a non-negative integer n. The drop returns all the elements after the given index while the take returns all the elements until the given index.

firstTenNumbers.take(3); // 0, 1, 2
firstTenNumbers.drop(3); // 3, 4, 5, 6, 7, 8, 9</span>

The functions drop and take can be specified by the following function:

list.take(n) + list.drop(n) == list</span>

dropWhile and takeWhile

They are technically drop and take but instead of taking a number as an argument, they take a predicate function.

The takeWhile function will return all the values until the predicate returns true.

firstTenNumbers.takeWhile(i -> i % 2 == 0); // 0</span>

The dropWhile function will return all the values in the list after which the predicate returns true.

firstTenNumbers.dropWhile(i -> i % 2 == 0); // 1, 2, 3, 4, 5, 6, 7, 8, 9</span>

Converters

Often times, we need converters. The role of converters is to change the list from one form to another. That is from Mutable to Immutable or vice versa.

We can achieve that via toMutable (using toList, toMap, toSet, toBag) and toImmutable function respectively.

MutableList<T> iCanChange = Lists.mutable.with(T… args);
iCanChange.toImmutable(); // From now onwards I cannot Change
ImmutableList<T> iCannotChange = Lists.immutable.with(T… args);
iCannotChange.toMutable(); // From now on I can change</span>

We will need to reverse our list and the API provides us with toReversed function to achieve the same.

MutableList<T> normalList = Lists.mutable.with(T… args);
normalList.toReversed();</span>

The stack data structure is inbuilt in the library. We can convert the list into stack using toStack().

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
firstTenNumbers.toStack().pop(4); // 9, 8, 7, 6</span>

There are many other converters available like toSet, toSortedSet, toMap, toBiMap, and others


Zip

The function zip will take a pair of lists and convert them into list of pairs. That is:

MutableList<Integer> houseNumbers = Lists.mutable.with(123, 456, 789);
MutableList<String> owners = Lists.mutable.with(“Ram”, “Raj”, “Rahul”);
owners.zip(houseNumbers); // (Ram:123), (Raj:456), (Rahul:789)</span>

It is also important to note that the length of the lists need not be equal.

The function zip has many uses like getting the Scalar Product.


Select & Reject

select and reject are nothing but filters. Both of them will accept a predicate. The select select only those are true. The rejectselect only those are are returning false.

MutableList<Integer> evenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
 .select(i -> i % 2 == 0);
// 0, 2, 4, 6, 8
MutableList<Integer> oddNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
 .reject(i -> i % 2 == 0);
// 1, 3, 5, 7, 9</span>

Note you can also do rejectWith and selectWith with a predicate function.


Partition

A PartitionMutableCollection is the result of splitting a mutable collection into two mutable collections based on a Predicate. — Eclipse Collections

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println(firstTenNumbers.partition(i -> i % 2 == 0).getSelected()); // 0, 2, 4, 6, 8</span>

The partition accepts the predicate and will split the list based on the predicate.

Note you can also do partitionWith.


GroupBy

Sometimes we will need to group the elements together, we can use groupBy for this. The groupBy will accept the predicate function and groups the element based on the predicate’s return value.

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println(firstTenNumbers.groupBy(i -> i % 2 == 0 ? “even” : “odd”)); 
// {even=[0, 2, 4, 6, 8], odd=[1, 3, 5, 7, 9]}</span>

Collect

The collect is more or less analogous to map. It takes a function. Then applies the function on all the values of the list and returns a new list with the updated values.

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println(firstTenNumbers.collect(i -> i + 1));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</span>

We can even do flatCollect.


Distinct

Distinct as the name implies collect the distinct values in the array.

MutableList<Integer> distinctValueList = Lists.mutable.with(1, 1, 2, 3, 4).distinct();
// [1, 2, 3, 4]</span>

AnySatisfy, AllSatisfy, and NoneSatisfy

The any|all|noneSatisfy makes it easy to check and also it is evaluated lazily. For example

MutableList<Character> gradeList = Lists.mutable.with(‘A’, ‘A’, ‘F’, ‘B’);
Boolean isPass = gradeList.allSatisfy(c -> c != ‘F’); // False
Boolean isFail = gradeList.anySatisfy(c -> c == ‘F’); // True
Boolean isPass = gradeList.noneSatisfy(c -> c == ‘F’); // False</span>

Max, Min and SumOfInt

As the name implies they get the maximum, minimum and sumOfInt of the values in the list provided.

MutableList<Integer> firstTenNumbers = Lists.mutable.with(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println(firstTenNumbers.min()); // 0
System.out.println(firstTenNumbers.max()); // 9
System.out.println(firstTenNumbers.sumOfInt(i -> i)); // 45</span>

There is a lot of other APIs available, check out the full list here).

We will continue with even more in-depth tutorial about Eclipse Collections.

If you like this article, please leave a like or a comment. :heart:

If you feel there is something wrong / missing in the article feel free to comment :)

You can follow me on Twitter.