Count orders per status with Collectors.groupingBy
Given a list of orders, return how many orders sit in each status. Use the Stream API and Collectors.groupingBy so the whole thing is one terminal collect.
Constraints:
- a single stream pass — do not group, then loop again to count
- an empty input returns an empty map, not
null
enum Status { NEW, PAID, SHIPPED }
record Order(Status status, int amount) {}
Map<Status, Long> countByStatus(List<Order> orders) {
// your code here
}
Write the implementation.
groupingBy(classifier) applies the classifier to each element and collects elements into a Map<K, List<T>> keyed by its result. The two-arg groupingBy(classifier, downstream) feeds each bucket into a second collector instead of listing the elements, so groupingBy(Order::status, counting()) yields a Map<Status, Long> of counts in one stream pass: orders.stream().collect(groupingBy(Order::status, counting())).
- ✗Expecting a plain
Map<K, List<T>>and then looping again to count instead of passingcounting() - ✗Thinking
groupingBysorts keys or needs aComparablekey likeTreeMapdoes - ✗Believing the downstream collector changes the keys rather than reducing each value bucket
- →How does
groupingBy(classifier, mapping(...))differ fromgroupingBy(classifier, counting())? - →When would you pass a map supplier as the third argument to
groupingBy?
Solution
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
Map<Status, Long> countByStatus(List<Order> orders) {
return orders.stream()
.collect(groupingBy(Order::status, counting()));
}
How it works. groupingBy takes a classifier — the key function Order::status — and drops elements into buckets: the classifier's result becomes the map key. The one-arg form would collect a Map<Status, List<Order>>.
The second argument is a downstream collector applied to each bucket instead of accumulating a list. counting() reduces a bucket to its size, so the result is a Map<Status, Long>. It is all computed in one terminal collect, and an empty input honestly yields an empty map. If you wanted sums rather than lists you would swap the downstream for summingInt(Order::amount).