How to use groupingBy Collector in Java Streams

Hey, Tea lovers! Today we will talk about the groupingBy Collector method of Java Stream API. Yes, it is similar to GROUP BY of SQL since it groups and collects the objects based on given condition or value. In case you want to get familiar with the Stream API, I recommend you to read the post “Be More Functional with Java’s Functional Interfaces” and “Stream API: The Hero Without a Cape“. These will help you understand the post and might refresh your memory if you already know. Prepare your tea then, to sip and code.

groupingBy and groupingByConcurrent

Java Stream API is the best thing added to Java. It made things much more clear and being declarative in nature, made it more readable. One of the Terminal functions of the Stream is groupingBy. As I said, it works kind of similar to the GROUP BY in SQL, except it works on Stream. It collects the object V, the one which is available now in the pipeline, into the Map<K, List<V>>, where K is the value on which the V is grouped. So the objects which have or generate the same key get added in the list of the same key. The groupingBy is overloaded so you can modify the return Map instance however you like. Map<K, List<V>> is the simplest one.

groupingByConcurrent is same as groupingBy, however it is thread safe.

List<Integer> list = Arrays.asList(1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 8, 9, 0);

// Simplest groupingBy
System.out.println("Simplest groupingBy");
Map<String, List<Integer>> oddEvenNumbers = list.stream()
        //grouped with EVEN or ODD
        .collect(groupingBy(n -> n % 2 == 0 ? "EVEN" : "ODD"));
System.out.println("Even Numbers are : " + oddEvenNumbers.get("EVEN"));
System.out.println("ODD Numbers are : " + oddEvenNumbers.get("ODD"));

Outoput:

Even Numbers are : [2, 2, 4, 6, 8, 8, 0]
ODD Numbers are : [1, 1, 3, 5, 7, 9]

In the above code, I have grouped based on the EVEN and ODD value. As this method executes it generates either EVEN or ODD as the key for the numbers and gets added to the respective key’s List. I have used String here, but you can use any type of object such as Integer, some custom class of yours.

Now, what if We don’t want to just create a List instead of some different things? Like a Set, or we want a summation of those numbers. Well, you can do so using the overloaded groupingBy.

Be a groupingBy Pro

groupingBy also takes another argument which is another Collector. You can modify the value in the final map to any other value rather than a List.

I will go through some different examples. I am only using List<Integer>, defined in the first code block, as to keep things simpler.

Group into Set

// group into sets
System.out.println("group into sets");
Map<String, Set<Integer>> oddEvenWithSet = list.stream()
        .collect(groupingBy(n -> n % 2 == 0 ? "EVEN" : "ODD", toSet()));
System.out.println("Even Numbers are : " + oddEvenWithSet.get("EVEN"));
System.out.println("ODD Numbers are : " + oddEvenWithSet.get("ODD"));

Output:

group into sets
Even Numbers are : [0, 2, 4, 6, 8]
ODD Numbers are : [1, 3, 5, 7, 9]

Sum the Values

// sum the numbers
System.out.println("sum the numbers ");
Map<String, Integer> sumOddOrEvenSquares = list.stream()
        // lets convert to square
        .map(n -> n * n)
        //grouped with EVEN or ODD
        .collect(groupingBy(n -> n % 2 == 0 ? "EVEN" : "ODD"
                , Collectors.summingInt(Integer::intValue)
        ));
System.out.println("Even Numbers squared sum: " + sumOddOrEvenSquares.get("EVEN"));
System.out.println("ODD Numbers squared sum: " + sumOddOrEvenSquares.get("ODD"));

Output:

sum the numbers 
Even Numbers squared sum: 188
ODD Numbers squared sum: 166

Get Average of the Values

// lets take average
System.out.println("Group Average of the numbers");
Map<String, Double> averageOfOddEven = list.stream()
        //grouped with EVEN or ODD
        .collect(groupingBy(n -> n % 2 == 0 ? "EVEN" : "ODD"
                , Collectors.averagingInt(Integer::intValue)
        ));
System.out.println("Even Numbers average: " + averageOfOddEven.get("EVEN"));
System.out.println("ODD Numbers average: " + averageOfOddEven.get("ODD"));

Output:

Group Average of the numbers
Even Numbers average: 4.285714285714286
ODD Numbers average: 4.333333333333333

Nested Grouping

Grouped values can be grouped again if we pass the groupingBy collector in the second argument.

Here, we have grouped the numbers based on Odd and Even values in the first groupingBy. After that, we are regrouping based on if the number is greater and less than or equal to 5.

// group again
System.out.println("Group again with some greater than 5");
Map<String, Map<String, Set<Integer>>> oddEvenAndCompareTo5 = list.stream()
        .collect(groupingBy(n -> n % 2 == 0 ? "EVEN" : "ODD",
                groupingBy(n -> n > 5 ? "GT5" : "LT5", toSet())
        ));
Map<String, Set<Integer>> evenNumbers = oddEvenAndCompareTo5.get("EVEN");
Map<String, Set<Integer>> oddNumbers = oddEvenAndCompareTo5.get("ODD");

System.out.println("Even numbers greater than 5: " + evenNumbers.get("GT5"));
System.out.println("Odd numbers greater than 5: " + oddNumbers.get("GT5"));

System.out.println("Even numbers Less than or equal to 5: " + evenNumbers.get("LT5"));
System.out.println("Odd numbers Less than or equal to 5: " + oddNumbers.get("LT5"));

Output:

Group again with some greater than 5
Even numbers greater than 5: [6, 8]
Odd numbers greater than 5: [7, 9]
Even numbers Less than or equal to 5: [0, 2, 4]
Odd numbers Less than or equal to 5: [1, 3, 5]

Conlusion

We learned about groupingBy and did some various examples on it. It is just the basic, you can use it according to your requirement. groupingByConcurrent is the same as groupingBy but can be used in parallel Stream without any side-effects and uses ConcurrentMap.

The Stream API is filled with so many functions you don’t need to create your own algorithm for most of the common things. We also have partitioningBy() which we will discuss in another post. You can check the whole code describe here on GitHub or Full Project.

Other post related to Stream:

See you in next post.