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
andmutable
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>
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'
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);
You can instantiate the list with of
.
MutableList<Integer> firstTenNumbers = Lists.mutable.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
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
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
The functions drop
and take
can be specified by the following function:
list.take(n) + list.drop(n) == list
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
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
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
. We can achieve that via toImmutable
function.
MutableList<T> iCanChange = Lists.mutable.with(T... args);
ImmutableList<T> iCannotChange = iCanChange.toImmutable(); // From now onwards I cannot Change
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();
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
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]
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)
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
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
Note you can also do
rejectWith
andselectWith
.
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
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]}
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]
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]
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
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
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
.