DEV Community

Sendil Kumar
Sendil Kumar

Posted on • Updated on • Originally published at sendilkumarn.com

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.

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 will increase the learning curve of the language, providing too less will lead to cumbersome implementation by the users. So languages have to be very careful in what they provide.

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

Eclipse collections also 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
    • Eager (while optimized)
  • provides both immutable and mutable collections
  • provide highly optimized 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>
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

Ingredients

The Eclipse Collections consists of the following data structures:

  1. List
  2. Set
  3. Map
  4. BiMap
  5. MultiMap
  6. Bag
  7. Stack
  8. Pair and others

All those data structures include the following implementations:

  1. Mutable
  2. Immutable
  3. Lazy
  4. Parallel
  5. Ordered
  6. Sorted
  7. Fixed
  8. 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);
Enter fullscreen mode Exit fullscreen mode

You can instantiate the list with of.

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

Reterival

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

firstTenNumbers.get(4);       // 4
firstTenNumbers.getFirst();  // 0
firstTenNumbers.getLast();  // 9
Enter fullscreen mode Exit fullscreen mode

In functional paradigm, take | takeWhile | drop | dropWhile APIs are awesome because they help to 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 gives back all the elements until the index.

firstTenNumbers.take(3); // 0, 1, 2
firstTenNumbers.drop(3); // 3, 4, 5, 6, 7, 8, 9
Enter fullscreen mode Exit fullscreen mode

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

list.take(n) + list.drop(n) == list
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

convertors

Often times, we will 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 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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

[T], [U] =>  [T, U]
Enter fullscreen mode Exit fullscreen mode
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)
Enter fullscreen mode Exit fullscreen mode

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 Reject select 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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Note you can also do rejectWith and selectWith.

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
Enter fullscreen mode Exit fullscreen mode

The partition will accept 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]}
Enter fullscreen mode Exit fullscreen mode

collect

The collect is more or less analogous to map. It takes a predicate 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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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. ❀️

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

You can follow me on Twitter.

Top comments (0)