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
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.
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 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 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"));
Code language: PHP (php)
Output:
Even Numbers are : [2, 2, 4, 6, 8, 8, 0]
ODD Numbers are : [1, 1, 3, 5, 7, 9]
Code language: CSS (css)
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 an 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"));
Code language: JavaScript (javascript)
Output:
group into sets
Even Numbers are : [0, 2, 4, 6, 8]
ODD Numbers are : [1, 3, 5, 7, 9]
Code language: CSS (css)
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"));
Code language: JavaScript (javascript)
Output:
sum the numbers
Even Numbers squared sum: 188
ODD Numbers squared sum: 166
Get the 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"));
Code language: JavaScript (javascript)
Output:
Group Average of the numbers
Even Numbers average: 4.285714285714286
ODD Numbers average: 4.333333333333333
Code language: CSS (css)
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"));
Code language: JavaScript (javascript)
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]
Code language: CSS (css)
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:
- Iterating over List: Basic to Advance to Stream API
- Stream API: The Hero Without a Cape
- Be More Functional with Java’s Functional Interfaces
- Reduce NullPointerExceptions with Optional in Java 8 & Beyond
See you in the next post. HAKUNA MATATA!!!
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.