JVM Advent

The JVM Programming Advent Calendar

Hidden Treasures of Eclipse collections – 2019 edition

Eclipse Collections is an open source Java Collections framework. In this blog I am going to demonstrate five lesser known features of the framework. I published a similar blog in last year’s Java Advent Calendar. Please refer to the resources at the end of the blog for more information about the framework.

1. countBy(): When you want to find the count of a particular object you can use the countBy() API to get a Bag. The purpose of a Bag is to maintain mapping of an object to the count. A Bag can be used to query the count of an item in O(1) time. Bag also provides additional helpful API which helps with counting. Learn more about Bag data structure in this blog.

@Test
public void countBy()
{
    MutableList<String> strings =
            Lists.mutable.with("A", "B", "C", "A", "B", "A");
    Bag<String> stringToCount = strings.countBy(each -> each);
    assertEquals(3, stringToCount.occurrencesOf("A"));
    assertEquals(2, stringToCount.occurrencesOf("B"));
    assertEquals(1, stringToCount.occurrencesOf("C"));
    assertEquals(3, stringToCount.sizeDistinct());
    assertEquals(6, stringToCount.size());
}

2. reject(): When you want to choose the elements which do not satisfy a predicate you can use the reject() API. This API is provided for enhanced readability and to make it intuitive to developers. You can use reject() in place of using a select() and negation of a boolean condition. In essence all the elements which do not return true for a boolean condition will be chosen when using reject(). The output of reject(BooleanCondition) is the same as what you will get by doing a select(!someBooleanCondition).

@Test
public void reject()
{
    MutableList<Integer> numbers =
            Lists.mutable.with(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    MutableList<Integer> odds = numbers.reject(num -> num % 2 == 0);
    // reject pattern used to find odd numbers.
    // Notice there is no negation in the predicate.
    assertEquals(Lists.mutable.with(1, 3, 5, 7, 9), odds);

    MutableList<Integer> oddsUsingSelect =
            numbers.select(num -> num % 2 != 0);
    assertEquals(odds, oddsUsingSelect);
}

3. makeString(): When you want a configurable string representation of a RichIterable you can use makeString(). If you use makeString() without a delimiter, then a default delimiter of "comma space" ( ", " ) is used. If you want a specific delimiter you can pass it to makeString() and the output string will have the string representation where every element separated by a delimiter. The delimiter is not used if the size of Iterable is 1.

@Test
public void makeString()
{
    MutableList<Integer> nums = Lists.mutable.with(1, 2, 3);
    assertEquals("[1, 2, 3]", nums.toString());
    // Notice the difference: toString() vs makeString().
    // the ", " delimiter is used by default
    assertEquals("1, 2, 3", nums.makeString());

    // Delimiter of choice can be passed
    assertEquals("1;2;3", nums.makeString(";"));

    MutableList<Integer> singleElement = Lists.mutable.with(1);
    // Delimiter is not used for size = 1
    assertEquals("1", singleElement.makeString());
    assertEquals("1", singleElement.makeString(";"));
}

4. zip(): When you want to stitch two OrderedIterables together you can use zip(). The zip() API operates on two OrderedIterables and stitches them such that you get an OrderedIterable of Pair of elements. In the Pair, the first of Pair is the element from first OrderedIterable and the second of Pair is the element from second OrderedIterable. In case the OrderedIterables are of different sizes then the excess elements from the longer OrderedIterable are ignored. The output of zip() is an OrderedIterable of the same size as the smaller OrderedIterable.

@Test
public void zip()
{
    MutableList<Integer> nums = Lists.mutable.with(1, 2, 3);
    MutableList<String> strings =
            Lists.mutable.with("A", "B", "C");
    assertEquals(
            Lists.mutable.with(Tuples.pair(1, "A"),
                               Tuples.pair(2, "B"),
                               Tuples.pair(3, "C")),
            nums.zip(strings));
    assertEquals(
            Lists.mutable.with(Tuples.pair("A", 1),
                               Tuples.pair("B", 2),
                               Tuples.pair("C", 3)),
            strings.zip(nums));

    MutableList<Integer> numsSmallerSize =
            Lists.mutable.with(1);
    assertEquals(
            Lists.mutable.with(Tuples.pair(1, "A")),
            numsSmallerSize.zip(strings));
    assertEquals(
            Lists.mutable.with(Tuples.pair("A", 1)),
            strings.zip(numsSmallerSize));

    MutableList<String> stringsSmallerSize =
            Lists.mutable.with("A", "B");
    assertEquals(
            Lists.mutable.with(Tuples.pair(1, "A"),
                               Tuples.pair(2, "B")),
            nums.zip(stringsSmallerSize));
    assertEquals(
            Lists.mutable.with(Tuples.pair("A", 1),
                               Tuples.pair("B", 2)),
            stringsSmallerSize.zip(nums));
}

5. corresponds(): When you want to find if all elements of two OrderedIterables are equal according to a Predicate you can use the corresponds() API. The corresponds() API operates by first checking if the two OrderedIterables have the same size, if they have the same size then corresponding elements of both the OrderedIterables are evaluated using the Predicate passed to corresponds(). If the size of OrderedIterables is equal and the Predicate returns true for all elements then corresponds() returns true. If the size of OrderedIterables is not equal or the Predicate returns false for any element then corresponds() returns false.

@Test
public void corresponds()
{
    MutableList<Integer> lhs1 = Lists.mutable.with(1, 2, 3);
    MutableList<Integer> rhs1 = Lists.mutable.with(1, 2, 3);
    assertTrue(lhs1.corresponds(rhs1, Integer::equals));

    MutableList<Integer> lhs2 = Lists.mutable.with(1, 2, 3);
    MutableList<Integer> rhs2 = Lists.mutable.with(2, 4, 6);
    assertTrue(
            lhs2.corresponds(rhs2,
                             (lhs, rhs) -> rhs == 2 * lhs));
    assertFalse(
            lhs2.corresponds(rhs2,
                             (lhs, rhs) -> rhs == lhs * lhs));
    assertFalse(lhs2.corresponds(rhs2, Integer::equals));

    MutableList<Integer> lhs3 = Lists.mutable.with(1, 2);
    MutableList<Integer> rhs3 = Lists.mutable.with(1, 2, 3);
    assertFalse(lhs3.corresponds(rhs3, Integer::equals));
}

Eclipse Collections Resources:
Eclipse Collections comes with it’s own implementations of ListSet and Map. It also has additional data structures like MultimapBag and an entire Primitive Collections hierarchy. Each of our collections have a fluent and rich API for commonly required iteration patterns.

Author: Nikhil Nanivadekar

Lead Eclipse Collections: eclipse.org/collections, Java Champion. I enjoy hiking, skiing, reading. All opinions stated by me are my own.

Next Post

Previous Post

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2020 JVM Advent | Powered by Jetbrains LogoJetBrains & steinhauer.software Logosteinhauer.software

Theme by Anders Norén

%d bloggers like this: