自定义 collector 是 java stream api 中实现高级归约操作的核心能力,由 supplier、accumulator、combiner、finisher 和 characteristics 五部分构成,适用于分组统计带排序、多字段聚合、带状态去重计数等场景。

自定义 Collector 是 Java Stream API 中实现高级归约操作的核心能力,适用于需要控制累积过程、并发合并、结果类型转换等场景,比如分组统计带排序、多字段聚合、带状态的去重计数等。
理解 Collector 的五个核心组件
Collector 本质是一个函数式接口,由五部分构成:supplier(容器构造)、accumulator(元素累加)、combiner(并发合并)、finisher(结果封装)、characteristics(行为特征)。自定义时需明确每部分职责:
-
Supplier
:提供一个可变容器(如 ArrayList、StringBuilder或自定义对象),用于暂存中间结果 -
BiConsumer
:将流中每个元素 U“塞进”容器T,例如list.add(item) -
BiConsumer
:在并行流中合并两个同类型容器(必须满足结合律),如 list1.addAll(list2) -
Function
:将最终容器 T转为所需结果R,比如把Map<k list>></k>转成不可变视图 -
Set
:声明行为特征(如 CONCURRENT、UNORDERED、IDENTITY_FINISH),影响并行流优化策略
手写一个带最大值追踪的统计 Collector
假设要一次性统计列表中元素个数、总和、最大值——标准 Collectors.summarizingInt() 不支持最大值(仅限 IntSummaryStatistics),可自定义:
先定义结果类:
static class Stats {
long count = 0;
int sum = 0;
Integer max = null;
void accept(int x) {
count++;
sum += x;
max = (max == null) ? x : Math.max(max, x);
}
void combine(Stats other) {
count += other.count;
sum += other.sum;
max = (max == null) ? other.max : (other.max == null ? max : Math.max(max, other.max));
}
}
再构建 Collector:
Collector<integer stats> statsCollector =
Collector.of(
Stats::new,
Stats::accept,
Stats::combine,
Function.identity(), // 已是最终类型,无需 finisher
Collector.Characteristics.IDENTITY_FINISH
);</integer>
使用:stream.collect(statsCollector) 直接返回 Stats 实例。
处理复杂状态:带窗口的最近 N 条记录 Collector
若需收集流中最后 3 个非空字符串,并保持插入顺序(FIFO),容器可用 ArrayDeque<string></string>:
-
Supplier:
() -> new ArrayDeque(3) -
Accumulator:检查非空后
deque.offerLast(s),超长则deque.pollFirst() -
Combiner:并行场景下不适用(窗口有顺序依赖),应声明
UNORDERED并禁用并发(或抛异常提示) -
Finisher:转为
List或LinkedHashSet去重保留序
注意:此类 Collector 不具备 CONCURRENT 特性,强行用于并行流会导致逻辑错误。
复用与组合:基于 Collectors 的扩展
不必从零造轮子。常用技巧包括:
- 用
Collectors.collectingAndThen()包装已有 Collector,追加 finisher 处理(如转ImmutableList) - 用
Collectors.groupingBy()+ 自定义 downstream Collector 实现嵌套聚合 - 将多个 Collector 的结果合并到一个对象中:先分别 collect,再用
Collectors.collectingAndThen()统一组装
例如:同时获取平均值和去重数,可分别调用 averagingDouble() 和 mapping(..., counting()),再用 collectingAndThen 合成 DTO。










