Site icon JVM Advent

Hidden Treasures of Eclipse Collections 2021 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 have published similar blogs in Java Advent Calendars of 2018, 2019, and 2020. Please refer to the resources at the end of the blog for more information about the framework.

  1. selectWithIndex() and rejectWithIndex(): There are times when you need to filter/select elements of an OrderedIterable using the element data and the index of an element. In such cases you need access to the index of the element in addition to the element itself. You can use the selectWithIndex() iteration pattern in this case. In Eclipse Collections, we expose the compliment of select operation called as reject. You can use rejectWithIndex() when you want to pick only those elements which do not satisfy the predicate.
    @Test
    public void selectWithIndex() {
        var nums = Lists.mutable.with(1, 2, 3, 4, 5);
        var numIndexDivisibleBy3 = nums.selectWithIndex(
                (num, index) -> (num + index) % 3 == 0);
     
        Assertions.assertEquals(
                Lists.mutable.with(2, 5),
                numIndexDivisibleBy3);
    }
    @Test
    public void rejectWithIndex() {
        var nums = Lists.mutable.with(1, 2, 3, 4, 5);
        var numIndexNotDivisibleBy3 = nums.rejectWithIndex(
                (num, index) -> (num + index) % 3 == 0);
    
        Assertions.assertEquals(
                Lists.mutable.with(1, 3, 4),
                numIndexNotDivisibleBy3);
    }
  2. getOnly(): When you want to check that there exists only one element in a RichIterable and get that element you can use getOnly(). The getOnly() API will throw an exception if the RichIterable has no elements or has more than 1 element.
    @Test
    public void getOnly() {
        var nums = Lists.mutable.with(1, 2, 3);
    
        var evens = nums.select(num -> num % 2 == 0);
        Assertions.assertEquals(2, evens.getOnly());
    
        var odds = nums.reject(num -> num % 2 == 0);
        Assertions.assertThrows(
                IllegalStateException.class,
                odds::getOnly);
    
        Assertions.assertThrows(
                IllegalStateException.class,
                () -> Lists.mutable.empty().getOnly());
    }

    If you do not have a RichIterable you can use Iterate.getOnly()

    @Test
    public void getOnly() {
        var nums = Arrays.asList(1, 2, 3);
    
        var evens = nums.stream()
                .filter(num -> num % 2 == 0)
                .collect(Collectors.toList());
        Assertions.assertEquals(2, Iterate.getOnly(evens));
    
        var odds = nums.stream()
                .filter(num -> num % 2 != 0)
                .collect(Collectors.toList());
        Assertions.assertThrows(
                IllegalArgumentException.class,
                () -> Iterate.getOnly(odds));
    }
  3. topOccurrences(): When you want to find the top ‘n‘ occurring elements in a Bag you can use topOccurrences(). In the event of a tie, all of the items with the number of occurrences that match the occurrences of the last item will be returned.
    @Test
    public void topOccurrences() {
        var letters =
                Bags.mutable.with("a","b","c","c","d","d","e","e","e");
        var topTwoByOccurrences = letters.topOccurrences(2);
    
        MutableList<ObjectIntPair<String>> expected =
                Lists.mutable.with(
                        PrimitiveTuples.pair("e", 3),
                        PrimitiveTuples.pair("d", 2),
                        PrimitiveTuples.pair("c", 2));
    
        Assertions.assertEquals(
                expected,
                topTwoByOccurrences);
    }
  4. bottomOccurrences(): When you want to find the bottom ‘n’ occurring elements in a Bag you can use bottomOccurrences(). In the event of a tie, all of the items with the number of occurrences that match the occurrences of the last item will be returned.
    @Test
    public void bottomOccurrences() {
        var letters =
                Bags.mutable.with("a","b","c","c","d","d","e","e");
        var bottomTwoByOccurrences = letters.bottomOccurrences(2);
    
        MutableList<ObjectIntPair<String>> expected =
                Lists.mutable.with(
                        PrimitiveTuples.pair("b", 1),
                        PrimitiveTuples.pair("a", 1));
    
        Assertions.assertEquals(
                expected,
                bottomTwoByOccurrences);
    }
  5. Interval.fromTo(): When you need integer elements starting from a number to a number you can use Interval. In Eclipse Collections Interval is a unique implementation which behaves as lazy as well as eager at the same time because it extends AbstractLazyIterable and implements List.
    @Test
    public void intervalFromTo() {
        Interval interval = Interval.fromTo(1, 3);
    
        Assertions.assertEquals(
                Lists.mutable.with(1, 2, 3),
                interval.toList());
    }

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.

Exit mobile version