文章目录
- 271. Java Stream API - 理解 Java Stream 的流水线模型:中间操作 vs 终端操作
- 🧱 中间操作(`Intermediate Operation`):搭建流水线,不启动
- 🔚 终端操作(Terminal Operation):触发流水线,处理数据
- 🔁 串联多个中间操作
- ⚠️ 常见误区:一个 Stream 只能使用一次!
- 🛠 正确方式:**复用数据源,不复用 Stream 对象**
- 🎯 中间 vs 终端操作对比总结
- 💡 比喻辅助记忆:
- 📌 补充建议:可练习的小题目
271. Java Stream API - 理解 Java Stream 的流水线模型:中间操作 vs 终端操作
在Java的Stream API中,我们构建的是一个“流水线式的数据处理过程”,就像搭建一条工厂生产线:
- 每一道工序是一个中间操作(
intermediate operation) - 最后一道是一个终端操作(
terminal operation),它才启动整条流水线。
🧱 中间操作(Intermediate Operation):搭建流水线,不启动
定义:一个方法返回一个Stream,就是一个中间操作。
作用:添加一个“处理环节”,但本身不触发数据处理。
Stream<String>stream=List.of("a","bb","ccc").stream();Stream<Integer>lengths=stream.map(str->str.length());// 中间操作✅ 你只是“配置”了处理流程,没有处理数据。就像装了一个“长度计算”的机械臂,还没开电源。
🔚 终端操作(Terminal Operation):触发流水线,处理数据
定义:一个方法返回的不是Stream(或是void),那就是终端操作。
作用:触发数据从源头一个个“通过中间操作”,并产出结果。
inttotal=List.of("a","bb","ccc").stream().map(String::length)// 中间操作.reduce(0,Integer::sum);// 终端操作System.out.println("总长度 = "+total);// 输出 6✅ 数据此时才真正流过map,然后被reduce聚合求和。
🔁 串联多个中间操作
varresult=List.of("apple","banana","avocado").stream().filter(s->s.startsWith("a"))// 过滤出以 a 开头的.map(String::toUpperCase)// 转大写.sorted()// 排序.toList();// 终端操作:生成结果列表System.out.println(result);// 输出: [APPLE, AVOCADO]📌 注意:这些.filter(),.map(),.sorted()每一步都只是排流程,只有.toList()才触发执行。
⚠️ 常见误区:一个 Stream 只能使用一次!
varstream=Stream.of(1,2,3,4);// 第一次使用,合法varmapped=stream.map(i->i+1);// 第二次使用,❌ 非法!stream 已经用过varlist=stream.toList();// 会抛 IllegalStateException💥 错误信息:
java.lang.IllegalStateException:stream has already been operated upon or closed🛠 正确方式:复用数据源,不复用 Stream 对象
// 正确做法:重新从集合创建新 Streamvarsource=List.of(1,2,3,4);varmapped=source.stream().map(i->i+1).toList();// ✅varfiltered=source.stream().filter(i->i%2==0).toList();// ✅🎯 中间 vs 终端操作对比总结
| 操作类型 | 特征描述 | 是否触发处理 | 返回值类型 | 示例方法 |
|---|---|---|---|---|
| 中间操作 | 构建流水线,返回新的 Stream | ❌ 否 | Stream | map,filter,sorted |
| 终端操作 | 启动流水线,返回结果或副作用 | ✅ 是 | 非Stream类型 | collect,toList,reduce,forEach |
💡 比喻辅助记忆:
- 中间操作:就像在工厂安装传送带上的“加工模块”,还没有开机。
- 终端操作:按下启动按钮,产品才真正开始加工和流转。
📌 补充建议:可练习的小题目
- 使用
map()和filter()构建一个处理流程,但不要调用终端操作,验证是否执行了逻辑? - 设计一个流程:过滤出偶数,再平方,然后求和。
- 尝试复用一个
Stream对象,观察异常,并改用集合重新创建 Stream。