JVM Advent

The JVM Programming Advent Calendar

Hidden Treasures of Eclipse Collections 2025 Edition

Eclipse Collections is an open source Java Collections framework. In this blog I am going to demonstrate four lesser known features of the framework. I have published similar blogs in Java Advent Calendars of 2018, 2019, 2020, 2021, 2022, 2023, and 2024. Please refer to the resources at the end of the blog for more information about the framework. The newly published Eclipse Collections Categorically: Level up your programming game is a great book to dive deep in the design and methodology behind the iteration patterns of Eclipse Collections.

  1. groupByUniqueKey(): Eclipse Collections offers a way to transform and collect the output in a map, where the key is the transformed value. The groupByUniqueKey() API ensures that the generated keys must each be unique, or else an exception is thrown. This is an easy way to create a map from a collection and guarantee that no keys are overridden.
    @Test
    public void groupByUniqueKey() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableMap<Integer, Integer> groupByUniqueKeyMap =
            list.groupByUniqueKey(
                each -> -1 * each); // Negate each integer
        
        MutableMap<Integer, Integer> expectedMap =
            Maps.mutable.of(
                -1, 1, //key, value pairs
                -2, 2,
                -3, 3,
                -4, 4);
        assertEquals(expectedMap, groupByUniqueKeyMap);
    }
    
    @Test
    public void groupByUniqueKey_throws() {
        MutableList list = Lists.mutable.of(1, 2, 2, 3);
        
        // Throws exception because the element 2 is a duplicate
        assertThrows(
                IllegalStateException.class,
                () -> list.groupByUniqueKey(each -> each));
    }
    
  2. countByEach(): Eclipse Collections offers a way to count the number of occurrences of each value after transforming each element of a collection. The countByEach() API does it by iterating through the collection and applying the function to each element and then counting the number of occurrences of the transformed value. This API returns a Bag which is an optimized data structure for counting number of objects. This is similar to the countBy() API that I covered in the 2019 blog with the difference that in case of countByEach() the transformation function returns an Iterable
    @Test
    public void countByEach() {
        MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4);
    
        MutableBag<Integer> counts = list
            .countByEach(
                each -> Lists.mutable.of(each, each + 1));
    
        assertEquals(1, counts.occurrencesOf(1));
        assertEquals(2, counts.occurrencesOf(2));
        assertEquals(2, counts.occurrencesOf(3));
        assertEquals(2, counts.occurrencesOf(4));
        assertEquals(1, counts.occurrencesOf(5));
    }
  3. sumBy*(): Eclipse Collections offers a way to group and sum elements of the collection using sumByInt(), sumByLong(), sumByDouble(), and sumByFloat() methods. A salient point to note is that the return types are up-casted i.e. sumByInt() returns a ObjectLongMap and sumByFloat() returns a ObjectDoubleMap. This avoids overflow issues in the results.
    // Common function to classify even and odd numbers
    Function<Integer, String> mappingFunction = each ->
        {
            if (each % 2 == 0) {
                return "EVEN";
            }
            return "ODD";
        };
    
    @Test
    public void sumByInt() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableObjectLongMap sumByInt = list.sumByInt(
                mappingFunction,
                each -> each);
    
        MutableObjectLongMap expected =
            ObjectLongMaps.mutable.of(
                "EVEN", 6L, //key, value pairs
                "ODD", 4L);
        assertEquals(expected, sumByInt);
    }
    
    @Test
    public void sumByLong() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableObjectLongMap sumByLong = list.sumByLong(
                mappingFunction,
                Integer::longValue);
    
        MutableObjectLongMap expected =
            ObjectLongMaps.mutable.of(
                "EVEN", 6L, //key, value pairs
                "ODD", 4L);
        assertEquals(expected, sumByLong);
    }
    
    @Test
    public void sumByDouble() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableObjectDoubleMap sumByDouble = list.sumByDouble(
                mappingFunction,
                Integer::doubleValue);
    
        MutableObjectDoubleMap expected =
            ObjectDoubleMaps.mutable.of(
                "EVEN", 6.0, //key, value pairs
                "ODD", 4.0);
        assertEquals(expected, sumByDouble);
    }
    
    @Test
    public void sumByFloat() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableObjectDoubleMap sumByFloat = list.sumByFloat(
                mappingFunction,
                Integer::floatValue);
    
        MutableObjectDoubleMap expected =
            ObjectDoubleMaps.mutable.of(
                "EVEN", 6.0, //key, value pairs
                "ODD", 4.0);
        assertEquals(expected, sumByFloat);
    }
    
  4. aggregateBy(): Eclipse Collections offers a way to aggregate and group results into a map using a grouping function. Please note the API signature — the first input is the grouping function, second input is the zero value function, and the last input is the aggregation function. The code below shows how each of these inputs impacts the behavior.
    @Test
    public void aggregateBy() {
        MutableList list = Lists.mutable.of(1, 2, 3, 4);
        MutableMap<String, Integer> aggregateBy1 =
            list.aggregateBy(
                mappingFunction, () -> 0, Integer::sum);
    
        // Note that because the zero value is in this case 0,
        // the result is that
        // sum of even numbers is 0 + 2 + 4 = 6; 
        // sum of odd numbers is 0 + 1 + 3 = 4
        MutableMap<String, Integer> expected1 = Maps.mutable.of(
                "EVEN", 6,
                "ODD", 4);
    
        assertEquals(expected1, aggregateBy1);
    
        MutableMap<String, Integer> aggregateBy2 =
            list.aggregateBy(
                mappingFunction, () -> 10, Integer::sum);
    
        // Note that because the zero value is in this case 10,
        // the result is that 
        // sum of even numbers is 10 + 2 + 4 = 16; 
        // sum of odd numbers is 10 + 1 + 3 = 14
        MutableMap<String, Integer> expected2 = Maps.mutable.of(
                "EVEN", 16,
                "ODD", 14);
    
        assertEquals(expected2, aggregateBy2);
    
        MutableMap<String, Integer> aggregateBy3 =
            list.aggregateBy(
                mappingFunction, 
                () -> 0, 
                (before, each) -> before + each + 10);
    
        // Note that because the aggregation function adds 10 to
        // every computation, the computation becomes: 
        // for even numbers: 0 + (2 + 10) + (4 + 10) = 26; 
        // for odd numbers: 0 + (1 + 10) + (3 + 10) = 24
        MutableMap<String, Integer> expected3 = Maps.mutable.of(
                "EVEN", 26,
                "ODD", 24);
    
        assertEquals(expected3, aggregateBy3);
    }

Summary:

In this blog I explained a few lesser known features of Eclipse Collections groupByUniqueKey(), countByEach(), sumBy*(), and aggregateBy(). I hope you found the post informative. If you have not used Eclipse Collections before, give it a try. There are few resources below. Make sure you show us your support and put a star on our GitHub Repository.

Eclipse Collections Resources

Eclipse Collections comes with it’s own implementations of List, Set and Map. It also has additional data structures like Multimap, Bag 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.

© 2025 JVM Advent | Powered by steinhauer.software Logosteinhauer.software

Theme by Anders Norén