Java Stream收集器是具备Supplier、Accumulator、Combiner、Finisher四方法及CONCURRENT/UNORDERED/IDENTITY_FINISH特性的可并行协作机制,用于安全高效地累积与转换流元素。

Java Stream API 中的收集器(Collector)不是简单“把元素塞进集合”,而是一套可组合、可并行、有明确生命周期的协作机制。它通过四个核心方法协同工作,完成从流元素到目标结果的转换。
收集器的四个核心角色
每个 Collector 都必须实现 Supplier、Accumulator、Combiner 和 Finisher 这四个接口方法,它们各司其职:
- supplier():提供一个空容器(如 ArrayList、StringBuilder 或自定义对象),作为中间累积的起点;
-
accumulator():定义如何把单个流元素“加进去”,例如
List::add或StringBuilder::append; - combiner():仅在并行流中起作用,负责合并两个独立线程产生的部分结果(如把两个 ArrayList 合并成一个);
-
finisher():将累积完成的中间容器(A 类型)转为最终结果(R 类型),比如把 StringBuilder 转成 String;若 A 与 R 类型相同且无需转换,可声明
IDENTITY_FINISH特性以跳过此步。
特性(Characteristics)影响执行行为
Collector 的 characteristics() 方法返回一个不可变 Set,告诉 Stream 框架该收集器是否具备某些优化能力:
- CONCURRENT:表示支持多线程并发累加(此时 supplier 返回的容器本身需线程安全,如 ConcurrentHashMap);
- UNORDERED:表示结果不依赖输入顺序(如 toSet()),允许 Stream 在并行时跳过排序开销;
- IDENTITY_FINISH:表示 finisher 是恒等函数(如 toList() 中 ArrayList 就是最终结果),可省略转换步骤。
例如 Collectors.toSet() 声明了 UNORDERED,而 Collectors.joining() 不带该特性——因为字符串拼接默认保持顺序。
立即学习“Java免费学习笔记(深入)”;
内置收集器的典型实现逻辑
Java 提供的常用收集器并非黑盒,其内部结构清晰可追溯:
-
toList()使用ArrayList::new作容器,List::add累加,(left, right) -> { left.addAll(right); }合并,并标记CH_ID(标识为有序、非并发、需 finisher); -
joining()底层用StringBuilder,高效追加,finisher 是StringBuilder::toString,且不声明IDENTITY_FINISH(因类型不同); -
toUnmodifiableList()在toList()基础上多一层包装,finisher 返回java.util.Collections.unmodifiableList(list)。
自定义收集器的关键注意事项
手写 Collector 时容易忽略几个实践要点:
- 避免在 accumulator 中做耗时操作(如远程调用、IO),否则会拖慢整个流;
- combiner 必须满足结合律:
combiner(combiner(a,b),c) == combiner(a,combiner(b,c)),否则并行结果不可靠; - 若希望支持并行,supplier 返回的容器应天然支持并发(或由 combiner 安全合并),而非靠 synchronized 包裹;
- 不要在 supplier 中预分配过大容量——Stream 不暴露元素总数,预估失误反而浪费内存。


















