提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
-
- [一、为什么要使用 Stream](#一、为什么要使用 Stream)
-
- [对比:传统 for 循环 vs Stream](#对比:传统 for 循环 vs Stream)
- [二、Stream 工作原理](#二、Stream 工作原理)
- [三、Stream 操作分类](#三、Stream 操作分类)
-
- [1. 中间操作(返回 Stream)](#1. 中间操作(返回 Stream))
- [2. 终端操作(返回最终结果)](#2. 终端操作(返回最终结果))
- [四、Stream vs 传统 for 循环性能](#四、Stream vs 传统 for 循环性能)
-
- 性能对比
- [并行 Stream](#并行 Stream)
- 五、总结
前言
stream() 是 Java 8 引入的 Stream API ,用于对集合进行声明式、函数式的数据处理。下面详细解释其原理和使用优势:
一、为什么要使用 Stream
对比:传统 for 循环 vs Stream
传统方式:
java
for (ErpUseDepartment dept : departments) {
if (dept == null) continue;
if (StrUtil.equals(
StringUtils.trim(document.getDeptCode()),
StringUtils.trim(dept.getDepartmentId())
)) {
return dept;
}
}
return null;
Stream 方式:
java
return departments.stream()
.filter(Objects::nonNull)
.filter(item -> StrUtil.equals(
StringUtils.trim(document.getDeptCode()),
StringUtils.trim(item.getDepartmentId())
))
.findFirst()
.orElse(null);
优势对比:
| 维度 | 传统 for 循环 | Stream API |
|---|---|---|
| 代码可读性 | 冗长,需手动管理循环 | 简洁,声明式表达意图 |
| 并行处理 | 需手动实现 | 只需 parallelStream() |
| 函数式组合 | 困难 | 支持链式调用 |
| 代码量 | 多 | 少 |
二、Stream 工作原理
核心概念
js
数据源 → 中间操作 → 终端操作
│ │ │
▼ ▼ ▼
List filter() findFirst()
map() collect()
sorted() count()
执行流程
步骤1:创建 Stream
java
departments.stream() // 将 List 转换为 Stream
步骤2:中间操作(惰性求值)
java
.filter(Objects::nonNull) // 过滤 null,不立即执行
.filter(item -> ...) // 过滤匹配条件,不立即执行
关键特点 :中间操作是惰性的,不会立即执行,只是构建操作流水线。
步骤3:终端操作(触发执行)
java
.findFirst() // 触发整个流水线执行
.orElse(null) // 处理 Optional 结果
关键特点 :只有遇到终端操作时,才会一次性执行整个流水线。
内部执行机制
java
departments.stream()
.filter(Objects::nonNull) // 操作1
.filter(item -> ...) // 操作2
.findFirst() // 终端操作
.orElse(null);
实际执行过程:
| 元素 | 操作1 | 操作2 | 结果 |
|---|---|---|---|
| dept1 | 通过 | 不匹配 | 继续 |
| dept2 | 通过 | 不匹配 | 继续 |
| dept3 | 通过 | 匹配 | 立即返回 dept3 |
| dept4 | 未执行 | 未执行 | - |
短路特性:一旦找到匹配元素,立即终止遍历,不会处理剩余元素。
三、Stream 操作分类
1. 中间操作(返回 Stream)
| 操作 | 说明 |
|---|---|
filter(Predicate) |
过滤元素 |
map(Function) |
转换元素 |
sorted(Comparator) |
排序 |
distinct() |
去重 |
limit(long) |
限制数量 |
2. 终端操作(返回最终结果)
| 操作 | 说明 |
|---|---|
findFirst() |
返回第一个元素(Optional) |
findAny() |
返回任意元素(Optional) |
collect(Collector) |
收集为集合 |
count() |
计数 |
forEach(Consumer) |
遍历消费 |
四、Stream vs 传统 for 循环性能
性能对比
| 场景 | 性能 | 说明 |
|---|---|---|
| 小规模数据 | 几乎相同 | Stream 有轻微开销 |
| 大规模数据 | Stream 可能更快 | 可并行化 |
| 多核环境 | Stream 优势明显 | parallelStream() 自动并行 |
并行 Stream
只需将 stream() 改为 parallelStream():
java
return departments.parallelStream() // 并行处理
.filter(Objects::nonNull)
.filter(item -> ...)
.findFirst()
.orElse(null);
五、总结
为什么使用 Stream?
- 代码更简洁:用声明式代码表达"做什么",而非"怎么做"
- 可读性更强:链式调用清晰表达数据处理流程
- 易于并行化:一键开启并行处理
- 函数式编程:支持 Lambda 表达式,符合现代编程风格
工作原理:
- 惰性求值:中间操作不立即执行
- 短路优化:终端操作时按需处理,找到结果立即停止
- 流水线执行:元素逐个经过所有操作,而非先执行完一个操作再执行下一个