Collectors本身不可叠加,但可通过嵌套、链式调用或组合实现复杂聚合;典型方式是groupingBy分组后对每组应用另一收集器,如统计平均薪资、最高薪员工等;toMap需指定mergeFunction处理键冲突;mapping与collectingAndThen用于中间或最终结果加工;支持多级分组及partitioningBy布尔分组。

Collectors收集器本身不是“可叠加”的对象,但通过嵌套、链式调用或组合多个收集器逻辑,可以实现复杂的数据整理目标。关键不在于把两个Collector直接相加,而在于利用其设计机制——比如用groupingBy生成分组结构后,再对每个分组内部应用另一个收集器(如mapping、collectingAndThen或reducing),从而达成多层聚合效果。
分组后做二次聚合
这是最典型也最实用的组合方式。例如:按部门分组后,不仅列出员工列表,还要统计每组平均薪资、最高薪资或汇总总人数。
-
统计每部门平均薪资:
groupingBy(Employee::getDept, averagingDouble(Employee::getSalary)) -
每部门薪资总和 + 员工姓名拼接:
groupingBy(Employee::getDept, collectingAndThen(summingDouble(Employee::getSalary), Math::round))和groupingBy(Employee::getDept, mapping(Employee::getName, joining(", "))) -
每部门取最高薪员工(完整对象):
groupingBy(Employee::getDept, collectingAndThen(maxBy(comparingDouble(Employee::getSalary)), Optional::get))
toMap配合mergeFunction处理冲突
当键可能重复时,单纯toMap(keyMapper, valueMapper)会抛异常。这时需显式提供合并策略,本质是把“冲突处理逻辑”作为第三个参数注入。
- 保留第一个值:
(oldValue, newValue) -> oldValue - 保留最后一个值:
(oldValue, newValue) -> newValue - 合并为集合(单键多值):
(oldList, newList) -> { oldList.addAll(newList); return oldList; },常配合supplier用HashMap::new或LinkedHashMap::new
自定义复合结果:collectingAndThen与mapping
这两个收集器常用来“加工中间结果”。mapping在归约前转换元素类型;collectingAndThen在归约完成后做最终封装,适合类型转换或格式化。
立即学习“Java免费学习笔记(深入)”;
- 将员工流转为“部门→员工名列表”,再统一转成大写:
groupingBy(Employee::getDept, mapping(e -> e.getName().toUpperCase(), toList())) - 统计数量后转为字符串描述:
counting() → collectingAndThen(counting(), c -> "共 " + c + " 人") - 构建不可变Map:
toMap(..., HashMap::new) → collectingAndThen(..., Collections::unmodifiableMap)
多级分组与嵌套收集
借助groupingBy的重载形式,可实现两层甚至三层分组,并在最内层指定任意收集器。
- 先按部门、再按职级分组,每组收集为Set:
groupingBy(Employee::getDept, groupingBy(Employee::getLevel, toSet())) - 部门→职级→薪资区间分布:
groupingBy(Employee::getDept, groupingBy(e -> e.getSalary() > 15000 ? "高" : "普通", counting())) - 结合
partitioningBy做布尔分组,内部再聚合:partitioningBy(e -> e.getAge() >= 35, mapping(Employee::getName, joining(" & ")))


















