Java Stream 流
来源
- 触发点:for 循环不够简洁,学习 Stream 流尝试完成需求
- 首次记录:2025-10-31
- 作者:huan
洞见
Stream 不是数据结构,而是一种计算模型。它将“做什么”(What)与“怎么做”(How)分离,使代码更接近业务语义,而非底层循环逻辑。
“用声明式代替命令式,让集合处理像写 SQL 一样自然。”
详情
核心概念
- 流(Stream):对数据源(集合、数组等)的只读、一次性、惰性操作序列。
- 中间操作(Intermediate):如
filter()、map()、sorted(),返回新 Stream,可链式调用。 - 终止操作(Terminal):如
collect()、forEach()、count(),触发实际计算并返回结果或 void。 - 惰性求值(Lazy Evaluation):中间操作不立即执行,直到遇到终止操作才“流水线”式处理。
- 不可复用性:一个 Stream 只能被消费一次,重复使用会抛出
IllegalStateException。
典型案例
// 案例1:过滤 + 转换 + 拼接
String names = users.stream()
.filter(u -> u.getAge() >= 18)
.map(User::getName)
.filter(Objects::nonNull)
.sorted()
.collect(Collectors.joining(", "));
// 案例2:分组统计
Map<Department, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));
// 案例3:并行处理(慎用)
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();预设使用场景
- 数据清洗(去重、过滤空值)
- 字段映射(ID → 名称、DTO 转换)
- 聚合计算(求和、平均、最大最小)
- 分组与分区(按条件分类统计)
- 快速生成字符串(如日志、报表拼接)
理论操作
| 操作类型 | 方法示例 | 功能说明 |
|---|---|---|
| 创建流 | list.stream(), Stream.of(), Files.lines() | 从集合/数组/IO 生成流 |
| 转换 | map(), flatMap() | 元素一对一/一对多映射 |
| 过滤 | filter(), distinct(), limit(), skip() | 筛选或截断元素 |
| 排序 | sorted(), sorted(Comparator) | 自然序或自定义排序 |
| 聚合 | collect(), reduce(), count(), anyMatch() | 终止并生成结果 |
实践日志
- 2024-03:首次在订单导出功能中用
Stream.map + Collectors.joining替代 for 循环拼接,代码行数减少 60%。 - 2024-08:误用
parallelStream()导致线程安全问题,后改回串行流 + 显式线程池控制。 - 2025-01:结合
Optional与filter(Objects::nonNull)避免 NPE,提升健壮性。
我的想法
对我影响
- 思维转变:从“如何遍历”转向“我要什么”,代码更聚焦业务逻辑。
- 可读性提升:链式调用使意图清晰,新人也能快速理解数据处理流程。
- 函数式入门:为后续学习 RxJava、Project Reactor 打下基础。
潜在问题
- 性能陷阱:过度链式操作可能创建过多中间对象;
parallelStream在小数据集上反而更慢。 - 调试困难:Stream 链难以打断点,需借助
peek()辅助日志。 - 滥用风险:简单 for 循环更高效时,强行用 Stream 反而画蛇添足。
优化拓展
- 自定义 Collector:实现复杂聚合逻辑(如树形结构构建)。
- 结合 Optional:
stream.map(x -> Optional.ofNullable(...)).filter(Optional::isPresent)... - 工具封装:将常用模式(如非空拼接)封装为静态工具方法,提高复用性。
- 性能监控:对关键 Stream 操作加入耗时日志,避免线上性能瓶颈。
关联网络
- 上游技术:Lambda 表达式、函数式接口(
Function,Predicate) - 下游应用:Spring Data JPA 的
Streamable、响应式编程(Reactor) - 对比技术:
- 传统 for 循环:控制精细,但冗长
- Guava FluentIterable:类似思想,但非 JDK 原生
- SQL:Stream 是“内存中的 SQL”
演化日志
- v0.1 (2025-10-31):初始版本
待办事项
- 编写 Stream 性能对比测试(for vs Stream vs parallelStream)
- 封装通用非空字符串拼接工具类
StreamUtils.joinNonBlank() - 学习
Collectors.teeing()(Java 12+)实现一次遍历多结果聚合 - 在团队 Code Review 中推广 Stream 最佳实践清单