Java Grouping and Partitioning
A downstream collector decides what happens inside each group after a stream splits. counting, mapping and partitioningBy cover the counting, adapting and splitting forms of that job.
groupingBy with counting counts each group
The classifier gives each word its length, and counting counts the words in each length group. The entries print sorted by key, so the listing has a fixed order.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> words = List.of("cat", "dog", "elephant", "sun", "moon", "fish");
Map<Integer, Long> counts = words.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));
counts.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
}
}groupingBy with mapping collects names per letter
The classifier gives each name its starting letter. mapping passes each name through unchanged to a list collector for its group.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> names = List.of("Ana", "Adam", "Ben", "Bella", "Cara");
Map<Character, List<String>> byLetter = names.stream()
.collect(Collectors.groupingBy(name -> name.charAt(0),
Collectors.mapping(name -> name, Collectors.toList())));
byLetter.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
}
}partitioningBy splits passed from failed
partitioningBy tests each score against a pass mark of 60. Both lists print in encounter order, the order the scores arrived in.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> scores = List.of(45, 82, 67, 90, 38);
Map<Boolean, List<Integer>> parts = scores.stream()
.collect(Collectors.partitioningBy(score -> score >= 60));
System.out.println("Passed: " + parts.get(true));
System.out.println("Failed: " + parts.get(false));
}
}Example
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> words = List.of("apple", "fig", "banana", "kiwi", "plum", "date");
Map<Integer, Long> counts = words.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));
counts.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
Map<Boolean, List<String>> parts = words.stream()
.collect(Collectors.partitioningBy(word -> word.length() >= 5));
System.out.println("Long words: " + parts.get(true));
System.out.println("Short words: " + parts.get(false));
}
}Key points
- groupingBy puts elements into groups that share the same classifier result.
- The second argument of groupingBy is a whole collector, and it decides what each group collects.
- counting counts the elements inside each group.
- mapping adapts each element before a downstream collector collects it.
- partitioningBy splits a stream by a true-or-false test, so its map always has two keys.
- A grouped map has no promised print order, so sorted printing by key makes a listing stable.
In this example, one word list feeds both a grouped count and a partition. The counts print sorted by length, and the partition prints long and short words in encounter order.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does the second argument of groupingBy do?