294. Java Stream API - 对流进行归约

文章目录

  • [294. Java Stream API - 对流进行归约](#294. Java Stream API - 对流进行归约)
      • [🎯 什么是归约(Reduction)?](#🎯 什么是归约(Reduction)?)
      • [✅ 什么是终端操作?](#✅ 什么是终端操作?)
        • [🔔 注意事项:](#🔔 注意事项:)
    • [🧠 使用 `BinaryOperator` 对流进行归约](#🧠 使用 BinaryOperator 对流进行归约)
      • [🧪 示例一:经典求和](#🧪 示例一:经典求和)
      • [🔁 示例二:使用 BinaryOperator 改写](#🔁 示例二:使用 BinaryOperator 改写)
      • [🧪 示例三:计算最大值(MAX)](#🧪 示例三:计算最大值(MAX))
    • [☕ Stream API 中的 reduce() 方法](#☕ Stream API 中的 reduce() 方法)
      • [🧪 示例四:带初始值的 reduce(避免 Optional)](#🧪 示例四:带初始值的 reduce(避免 Optional))
    • [📌 小结](#📌 小结)
    • [🧠 延伸思考](#🧠 延伸思考)

294. Java Stream API - 对流进行归约

🎯 什么是归约(Reduction)?

在 Java Stream API 中,所谓的归约操作,其实就像 SQL 中的聚合操作 ------ 比如 SUM(), MAX() 等。我们通过一个终端操作(Terminal Operation )来将流中的多个元素聚合成一个结果。


✅ 什么是终端操作?

终端操作会触发整个流的执行,常见的包括:

  • collect()(如:collect(Collectors.toList()))
  • reduce()(本节主角)
  • forEach()
  • count()
  • anyMatch(), allMatch(), 等
🔔 注意事项:
  1. 没有终端操作的流不会执行任何处理!
    • 如果你看到一个只调用了 map() 或 filter() 但没有终端操作的流,那就是个 bug。
  2. 每个 Stream 实例只能执行一次终端操作!
    • 流一旦执行终端操作,就会"关闭",不能再使用,否则会抛出 IllegalStateException。

🧠 使用 BinaryOperator 对流进行归约

Java 中 reduce() 方法的核心,就是 使用一个二元操作符(BinaryOperator)将两个元素合并成一个,不断迭代直到只剩一个值。


🧪 示例一:经典求和

我们先用传统 for 循环来实现整数列表求和:

java 复制代码
List<Integer> ints = List.of(3, 6, 2, 1);

int sum = ints.get(0);
for (int i = 1; i < ints.size(); i++) {
    sum += ints.get(i);
}
System.out.println("sum = " + sum);

🟢 输出:

java 复制代码
sum = 12

🔍 这个过程就是不断把前面计算的"部分和"与下一个元素相加。


🔁 示例二:使用 BinaryOperator 改写

我们可以将"加法"逻辑抽象成一个 BinaryOperator:

java 复制代码
List<Integer> ints = List.of(3, 6, 2, 1);

BinaryOperator<Integer> sum = (a, b) -> a + b;

int result = ints.get(0);
for (int i = 1; i < ints.size(); i++) {
    result = sum.apply(result, ints.get(i));
}
System.out.println("sum = " + result);

🟢 输出:

java 复制代码
sum = 12

🎯 好处:只需要更换 BinaryOperator,就能实现其他聚合操作!


🧪 示例三:计算最大值(MAX)

只需将 BinaryOperator 换成一个"取较大值"的函数:

java 复制代码
List<Integer> ints = List.of(3, 6, 2, 1);

BinaryOperator<Integer> max = (a, b) -> a > b ? a : b;

int result = ints.get(0);
for (int i = 1; i < ints.size(); i++) {
    result = max.apply(result, ints.get(i));
}
System.out.println("max = " + result);

🟢 输出:

java 复制代码
max = 6

☕ Stream API 中的 reduce() 方法

现在让我们用 Stream 的 reduce() 方法 来实现相同逻辑。reduce() 有多个重载版本,这里我们使用最基础的一种:

java 复制代码
List<Integer> ints = List.of(3, 6, 2, 1);

Optional<Integer> sum = ints.stream()
                            .reduce((a, b) -> a + b);

sum.ifPresent(s -> System.out.println("sum = " + s));

🟢 输出:

java 复制代码
sum = 12

🔎 reduce() 返回的是 Optional<T>,因为当流为空时,没有结果。


🧪 示例四:带初始值的 reduce(避免 Optional)

java 复制代码
List<Integer> ints = List.of(3, 6, 2, 1);

int sum = ints.stream()
              .reduce(0, (a, b) -> a + b);

System.out.println("sum = " + sum);

🟢 输出:

java 复制代码
sum = 12

🎯 传入初始值后,reduce() 会从这个值开始进行归约,并返回确定的类型(非 Optional)。


📌 小结

特性 说明
✅ 终端操作 reduce() 是一种终端操作,用于聚合流元素
🔄 可变操作 只需变换 BinaryOperator 即可进行加总、取最大、最小等操作
💡 Optional 处理 不传初始值时返回 Optional,可避免空流 NPE
♻️ 不可复用 Stream 使用一次后即失效,请勿复用

🧠 延伸思考

  • reduce() 可以用于更多复杂的聚合计算,例如拼接字符串、合并集合等。
  • 在并行流中(parallelStream()),reduce() 的初始值需要满足结合律,以确保并发结果一致。
相关推荐
Flynn Liang9 分钟前
可观测性与成本治理:一次对话的 17 个观测点
java·langchain·可观测性·micrometer·langchain4j·token 成本·llm 监控
金銀銅鐵28 分钟前
带您看懂字节码指令(三):同一行加法,为什么有四种指令
java·jvm
another heaven3 小时前
【C/C++ Ranges 是零开销抽象吗?拆开管道、惰性求值和编译期膨胀】
c语言·开发语言·c++
文丑颜不良啊4 小时前
LeetCode2821~2840题目解析
java·leetcode
SomeOtherTime5 小时前
Rust 闭包语法和原理(AI回答)
开发语言·后端·rust
apcipot_rain5 小时前
C++转Java日记(10)
java·开发语言
总要学点什么6 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
简 洁 冬冬6 小时前
泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」
java·泛型
LGL6030A6 小时前
Java学习历程31——初识Spring Boot
java·spring boot·学习
xiaoye-duck6 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型