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 a 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.

I have also written a post on How to Use partitioningBy Collector in Java Stream API


You can follow me on social media via @coderstea on TwitterLinkedinFacebook, or Instagram. We also share high-quality videos about programming on our Youtube channel. You can also publish your post on CodersTea, just share your thought on Contact Us or let us know in the comments.


groupingBy and groupingByConcurrent

Java Stream API is the best thing added to Java. It made things much more clear and being declarative, made it more readable. One of the Terminal functions of the Stream is groupingBy. As I said, it works kind of similarly 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 to 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 the 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"));

Output:

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 objects such as Integer, or 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 Stream API 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, 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

//let's take an 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 with groupingBy

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]

Conclusion

We learned about groupingBy and did some various examples of it. It is just 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 algorithm for most of the common things. We also have partitioningBy() which we will discuss in another post. You can check the whole code described here on GitHub or Full Project.

Other posts related to Stream:

See you in the next post. HAKUNA MATATA!!!

You can follow me on social media via @coderstea on TwitterLinkedinFacebook, or Instagram. We also share high-quality videos about programming on our Youtube channel. You can also publish your post on CodersTea, just share your thought on Contact Us or let us know in the comments.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.

TWITTER FEED

INSTRAGRAM FEED

Newsletter

Subscribe my Newsletter for new blog posts, tips & new photos. Let's stay updated!

coderstea-logo-invert
@2022 All Right Reserved. Designed and Developed by CodersTea

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More