Collectors.teeing()是Java 12引入的增强方法,支持单次流遍历并行执行两个独立聚合操作,并通过合并函数整合结果,提升性能且保持声明式风格。

Java Stream API 中的 Collectors.teeing() 是 Java 12 引入的关键增强,它让一次流遍历就能完成两项独立聚合,避免重复过滤或多次遍历,既提升性能又保持代码简洁。
teeing() 的核心逻辑
它不是“先做A再做B”,而是“每个元素同时喂给两个收集器”,最后用合并函数把两个结果拼成一个最终对象。整个过程仍保持流式、不可变、声明式风格。
- 第一个参数:任意合法收集器(如
toList()、summingInt()、filtering()) - 第二个参数:另一个独立收集器,类型可与第一个不同
- 第三个参数:
BiFunction,接收前两个收集器的结果,返回最终类型
典型使用场景
适合需要从同一数据源提取两类信息并结构化组合的场合:
- 统计+明细:比如“总订单数 + 所有高价值订单列表”
- 极值聚合:如同时获取最小年龄和最大年龄,并封装为统一结果
- 分类汇总:筛选出满足条件的元素列表,同时计算其数量或总和
- 多维度报表:例如活动报名中,“确认参加者姓名列表”和“总参与人数”一次生成
写法要点与避坑提示
容易忽略但影响正确性的细节:
立即学习“Java免费学习笔记(深入)”;
- 两个下游收集器必须兼容同一输入类型
T;若需类型转换,应在teeing()外或内部用mapping()提前处理 - 合并函数中注意
Optional解包——比如minBy()和maxBy()返回Optional,需判空或用orElseThrow() - 若任一下游收集器是并发安全的(
CONCURRENT),且另一个也是,则整体结果收集器也支持并行流 - 不建议在
merger中做复杂业务逻辑或副作用操作,应专注结果组装
一个实用示例:订单分析
假设有一组订单,想同时得到“平均金额”和“超500元的订单ID列表”:
var result = orders.stream().collect(
Collectors.teeing(
Collectors.averagingDouble(Order::getAmount),
Collectors.filtering(o -> o.getAmount() > 500,
Collectors.mapping(Order::getId, Collectors.toList())),
(avg, highIds) -> new OrderSummary(avg, highIds)
)
);
这里没有 filter 后再 map,也没有两次 stream() 调用,单次遍历就完成全部计算。


















