Collectors.counting()是Java 8中配合groupingBy统计分组数量最简洁高效的方式,返回Map,适用于任意字段分组,但不支持null键和条件计数。

用 Collectors.counting() 统计分组后每组元素个数,是 Java 8 Stream API 中最简洁高效的方式之一。它专为配合 Collectors.groupingBy() 使用而设计,无需手写计数逻辑,也不依赖额外的 map 或循环。
核心用法:groupingBy + counting
直接将 Collectors.counting() 作为下游收集器传入 groupingBy,即可按指定键分组并自动统计每组数量:
- 适用于任意可分组的字段(字符串、枚举、数字、自定义对象等)
- 返回类型是
Map,注意计数值是Long而非Integer - 空集合或无匹配项时,对应分组不会出现在结果 Map 中(不是值为 0)
常见实战场景示例
按状态统计订单数量:
List<order> orders = ...;
Map<string long> countByStatus = orders.stream()
.collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));</string></order>
输出如:{PENDING=12, SHIPPED=45, DELIVERED=38}
按首字母统计姓名分布:
List<string> names = Arrays.asList("Alice", "Bob", "Anna", "Charlie");
Map<character long> countByFirstChar = names.stream()
.collect(Collectors.groupingBy(name -> name.charAt(0), Collectors.counting()));</character></string>
输出如:{A=2, B=1, C=1}
注意事项与避坑点
不能直接用于 null 键:如果分组键可能为 null,需提前过滤或映射,否则抛 NullPointerException
- 安全写法:
.filter(Objects::nonNull).collect(...) - 或使用
Collectors.groupingBy(keyMapper, LinkedHashMap::new, Collectors.counting())配合自定义 map 实现 null 容忍(需自行处理)
性能友好,但不支持条件计数:counting() 是全量计数;如需“只统计满足某条件的元素”,应改用 summingLong 或 reducing,例如:
Map<string long> paidCount = orders.stream()
.collect(Collectors.groupingBy(Order::getStatus,
Collectors.summingLong(o -> o.isPaid() ? 1L : 0L)));</string>
进阶组合:带排序或限制的分组计数
若需按数量降序排列 Top N 分组,可在收集后链式操作:
Map<string long> top3ByCount = orders.stream()
.collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<string long>comparingByValue().reversed())
.limit(3)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));</string></string>
这样得到的是保留顺序的 LinkedHashMap,适合做排行榜展示。










