297. Java Stream API - Java Stream API 中的 reduce() 方法详解

297. Java Stream API - Java Stream API 中的 reduce() 方法详解

在 Java Stream 中,reduce() 是一个非常核心的终端操作,它可以将流中的所有元素通过一个二元操作折叠成一个最终结果。Stream API 提供了 三个重载版本,今天我们将一一解析:


🎯 第一种:reduce(identity, accumulator)

这是最常用也最安全的一种形式,它接受两个参数:

T reduce(T identity, BinaryOperator<T> accumulator)

✅ 特点:

  • 提供了单位元(identity element),即当流为空时返回的默认值。
  • 不会返回 Optional,因为总能返回 identity。
  • 可用于并行流,并行友好。

📌 示例讲解:

List<Integer> ints = List.of(3, 6, 2, 1);
int sum = ints.stream().reduce(0, Integer::sum);
System.out.println("sum = " + sum); // 输出:12

这个过程等价于:

int result = 0;
for (int i : ints) {
    result = result + i;
}

即使 ints 是个空集合,reduce() 也会返回 0,不会抛异常!


⚠️ 注意:你提供的 identity 必须真的是该操作的单位元!

如果你写错了,Stream API 不会提示你错误,但结果会悄悄错掉!

错误示例:

Stream<Integer> ints = Stream.of(0, 0, 0, 0);
int sum = ints.reduce(10, Integer::sum); // ❌ 错误的 identity
System.out.println("sum = " + sum); // 输出 10,不是 0!

正确示例:

Stream<Integer> ints = Stream.of(0, 0, 0, 0);
int sum = ints.reduce(0, Integer::sum);
System.out.println("sum = " + sum); // ✅ 输出:0

✅ 提醒学员:identity 是你负责提供的,必须和操作一致!


🧩 第二种:reduce(accumulator) → 返回 Optional<T>

Optional<T> reduce(BinaryOperator<T> accumulator)

✅ 特点:

  • 没有 identity,所以返回结果可能为空(比如遇到空流)。
  • 使用 Optional 包装结果。

📌 示例:取最大值

Stream<Integer> ints = Stream.of(2, 8, 1, 5, 3);
Optional<Integer> optionalMax = ints.reduce((i1, i2) -> i1 > i2 ? i1 : i2);

System.out.println("result = " + optionalMax.orElseThrow());

🟢 输出:

result = 8

🚨 如果流为空怎么办?

Stream<Integer> empty = Stream.empty();
Optional<Integer> optional = empty.reduce(Integer::min);

System.out.println(optional.orElse(-1)); // 安全返回默认值 -1

✅ 推荐用法:

从 Java 10 起,推荐使用 orElseThrow() 替代 get()

optional.orElseThrow(); // 安全,可读性好

不推荐:

optional.get(); // 会抛出 NoSuchElementException

🔁 第三种:reduce(identity, accumulator, combiner) → 并行流专用

<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner);

✅ 特点:

  • 支持映射 + 累加一步完成。
  • identity 是 combiner 的单位元。
  • 可以把元素转换为另一种类型(泛型 U)。

🧠 背景理解:

适合并行流时,流被切分成多个片段,每个片段部分归约后,需要用 combiner 合并成总结果。


📌 示例:统计字符串长度总和

Stream<String> strings = Stream.of("one", "two", "three", "four");

int result = strings.reduce(
    0, // identity 是 0
    (partialSum, str) -> partialSum + str.length(), // accumulator
    Integer::sum // combiner
);

System.out.println("sum = " + result); // 输出:15

💡 提取 mapper 表达更清晰:

Function<String, Integer> mapper = String::length;
BiFunction<Integer, String, Integer> accumulator =
    (partialSum, str) -> partialSum + mapper.apply(str);

这样表达更清楚:映射 + 累加 分别负责什么角色。


🧠 三种 reduce() 方法对比表:

方法签名 返回类型 是否要求 identity 是否返回 Optional 空流行为 并行友好性
reduce(identity, accumulator) T ✅ 是 ❌ 否 返回 identity
reduce(accumulator) Optional<T> ❌ 否 ✅ 是 返回 Optional.empty() ❌ 不推荐
reduce(identity, accumulator, combiner) U ✅ 是 ❌ 否 返回 identity ✅ 强推荐并行用

📚 结语:什么时候用哪种?

需求 使用方法
你知道 identity,且只处理本类型 reduce(identity, accumulator)
没有 identity,比如 min()max() reduce(accumulator),处理 Optional
需要先映射再 reduce,或并行流合并 reduce(identity, accumulator, combiner)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容