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 表达式、函数式接口(FunctionPredicate
  • 下游应用: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 最佳实践清单