Using Eclipse Collections

Using Eclipse Collections

In this article, you will learn about some useful features provided by Eclipse Collections. Do you feel that Java Streams API is sometimes not enough? I think it’s worth taking a look at Eclipse Collections. Let’s discuss why.

Mutable or Immutable

If you like the Kotlin collections API this concept appeals to you. You can create mutable and immutable collections. Only mutable collections provide methods for adding new objects.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
MutableList<Person> persons = Lists.mutable.with(p1);
persons.add(p2);
persons.set(1, p3);

The same methods are not available for e.g. ImmutableList and cause compilation error.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
ImmutableList<Person> persons = Lists.immutable.with(p1);
persons.add(p2); // ERROR!
persons.set(1, p3); // ERROR!

Lazy collections

Another interesting feature of Eclipse Collections, called lazy collections, allows you to defer execution until a terminal operation is invoked. You can enable the lazy API by calling the asLazy method. Let’s see how it works. Firstly, we have the following class. I added a single log message inside the getAge method.

public class Person {

    private final String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        System.out.println("Age: " + this.age);
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Then let’s create a lazy collection. In the first step we increase the age of each person by 2. Then we filter the persons with age higher than 20. Finally, we get only a first element from a result stream.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
Person p4 = new Person("Test4", 30);
Person p5 = new Person("Test5", 35);
MutableList<Person> persons = Lists.mutable.with(p1, p2, p3, p4, p5);
persons
   .asLazy()
   .tap(p -> p.setAge(p.getAge() + 2))
   .select(p -> p.getAge() > 20)
   .getFirst();

What’s the output of the code visible above? Because we get only a first element from a list, it calls the getAge method only for a first filtered object.

Age: 20
Age: 22

Now, let’s do the same operation for non-lazy collection.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
Person p4 = new Person("Test4", 30);
Person p5 = new Person("Test5", 35);
MutableList<Person> persons = Lists.mutable.with(p1, p2, p3, p4, p5);
persons
   .tap(p -> p.setAge(p.getAge() + 2))
   .select(p -> p.getAge() > 20)
   .getFirst();

Here’s the result. We call the getAge method twice for every single object in the list although we finally take only a first object.

Age: 22
Age: 18
Age: 24
Age: 30
Age: 35
Age: 24
Age: 20
Age: 26
Age: 32
Age: 37

Collect elements

With Eclipse Collections we can easily collect and filter elements using a single method. Let’s consider the following operation with Java Streams. We have the input list with organizations. Then we want to filter only an organization with Test1 name, get all employees from this organization and then convert it to the list of employees.

Employee e1 = new Employee(1, "Test1", "Developer");
Employee e2 = new Employee(2, "Test2", "Developer");
Employee e3 = new Employee(3, "Test3", "Developer");
Employee e4 = new Employee(4, "Test4", "Developer");
Organization o1 = new Organization("Test1", List.of(e1, e2));
Organization o2 = new Organization("Test2", List.of(e3, e4));
List<Organization> organizations = List.of(o1, o2);
List<Employee> employees = organizations
   .stream()
   .filter(o -> o.name().equals("Test1"))
   .map(Organization::employees)
   .flatMap(List::stream)
   .collect(Collectors.toList());

With Eclipse Collections you can do the same thing using the following two methods.

MutableList<Employee> employees = organizations
   .select(o -> o.name().equals("Test1"))
   .flatCollect(Organization::employees);

Similarly, if you would like to collect employees without flatting with Java Streams you should do that as shown below.

List<List<Employee>> employees2 = organizations
   .stream()
   .filter(o -> o.name().equals("Test1"))
   .map(Organization::employees)
   .collect(Collectors.toList());

On the other hand, with Eclipse Collections you can do that using a single method collectIf.

MutableList<List<Employee>> employees2 = organizations
   .collectIf(o -> o.name().equals("Test1"), Organization::employees);

Arithmetic operations

With Eclipse Collections we can easily do some arithmetic operations like counting or searching for mix or max element. In the following fragment of code, we want to count the number of employees with a position Developer.

Employee e1 = new Employee(1, "Test1", "Developer");
Employee e2 = new Employee(2, "Test2", "Architect");
Employee e3 = new Employee(3, "Test3", "Developer");
Employee e4 = new Employee(4, "Test4", "Tester");
List<Employee> employees = List.of(e1, e2, e3, e4);
long c = employees
   .stream()
   .filter(emp -> emp.position().equals("Developer"))
   .count();

With Eclipse Collections we may use a single method once again.

MutableList<Employee> employees = Lists.mutable.of(e1, e2, e3, e4);
int c = employees.count(emp -> emp.position().equals("Developer"));

Similarly, with Java Streams looking for a minimal element in the list is a little bit complicated. We want to find employee with the lowest id.

Employee r1 = employees
   .stream()
   .min(Comparator.comparing(Employee::id))
   .orElseThrow(NoSuchElementException::new);

As you probably guess, we may do the same operation by calling a single method on Eclipse collection.

Employee r1 = employees.minBy(Employee::id);

Final Thoughts

I described only some selected methods provided by Eclipse Collections. For a detailed description of the whole API, you may refer to their documentation. Another reason that may convince you to switch to Eclipse Collections is resource usage. Following the documentation, it provides a memory-efficient implementation of sets, maps, and other primitive collections. Also if you are interested in other articles about Java you may read New Developer Friendly Features After Java 8.

Leave a Reply