stream的reduce是通用归约操作,含三种签名:无初值(返回optional)、带初值(需零值)、三参数(支持并行),须遵守结合律、无状态等原则,空流、线程安全和中间对象是常见陷阱。

Stream 的 reduce 方法不是简单求和工具,而是通用的“归约”操作核心——它把流中元素按你定义的规则逐步合并成一个结果。关键在于理解它的三种重载形式及适用场景,尤其要避免常见陷阱(比如空流、线程安全、中间对象创建)。
掌握 reduce 的三种签名与语义
1. 无初始值的 reduce(Optional 返回)Optional<t> reduce(BinaryOperator<t> accumulator)</t></t>
适用于非空流,返回 Optional 防止 NPE。accumulator 必须满足结合律和无状态(如 (a, b) -> a + b),否则结果不可预测。
2. 带初始值的 reduce(必返结果)T reduce(T identity, BinaryOperator<t> accumulator)</t>
identity 是“零值”(如求和用 0,拼接用 ""),必须满足:对任意 t,accumulator.apply(identity, t) == t。注意:identity 会被参与每次计算,所以不能是可变对象(如 new StringBuilder())。
3. 支持并行的三参数 reduce(含组合器)<u> U reduce(U identity, BiFunction<u super t u> accumulator, BinaryOperator<u> combiner)</u></u></u>
用于复杂类型归约(如统计、分组),combiner 负责合并各线程的局部结果。必须满足:combiner 可交换、结合,且与 accumulator 兼容。
实现自定义累加:从求和到多维度统计
✅ 正确示例:整数列表求和(带初始值)
List<integer> nums = Arrays.asList(1, 2, 3, 4); int sum = nums.stream().reduce(0, (a, b) -> a + b); // 返回 10</integer>
✅ 进阶示例:统计字符串长度总和 + 最长字符串(封装为自定义对象)
class Summary {
int totalLen;
String longest;
Summary(int totalLen, String longest) {
this.totalLen = totalLen;
this.longest = longest;
}
static Summary combine(Summary s1, Summary s2) {
return new Summary(
s1.totalLen + s2.totalLen,
s1.longest.length() >= s2.longest.length() ? s1.longest : s2.longest
);
}
}
List<string> words = Arrays.asList("hello", "world", "java");
Summary result = words.stream()
.reduce(
new Summary(0, ""),
(s, w) -> new Summary(s.totalLen + w.length(),
w.length() > s.longest.length() ? w : s.longest),
Summary::combine
);</string>
⚠️ 常见错误:
- 用
new ArrayList()当 identity —— 并行时多个线程共用同一对象,数据错乱 - accumulator 中修改入参对象(如
list.add(x))—— 违反无状态原则,结果不确定 - 忽略空流:用无初始值 reduce 处理空集合会抛 NoSuchElementException
替代方案:何时该用 collect 而非 reduce
当目标是构建集合、分组、汇总(如 toList(), groupingBy(), summarizingInt()),优先用 collect。它专为可变容器优化,支持并发收集,API 更直观。
例如累计字符串:
- ❌ 不推荐(创建过多 String 对象):
stream.reduce("", (a,b)->a+b, (a,b)->a+b) - ✅ 推荐:
stream.collect(Collectors.joining())或stream.collect(StringBuilder::new, (sb,s)->sb.append(s), (sb1,sb2)->sb1.append(sb2)).toString()
调试 reduce 的实用技巧
在 accumulator 中临时加日志(仅开发期):
stream.reduce(0, (a, b) -> {
System.out.printf("累加:%d + %d = %d%n", a, b, a + b);
return a + b;
});
注意:生产环境禁用,因 reduce 不保证执行顺序(尤其并行流),日志可能乱序;也别在其中做 I/O 或锁操作。
reduce 的本质是函数式归约——输入流、输出单值、过程纯且无副作用。写对 accumulator 和 combiner,比记住语法更重要。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











