17370845950

Java流式操作与Stream API的核心概念
Java Stream是一次性惰性计算管道,不触发终端操作则中间操作不执行,不可重用,重复使用抛IllegalStateException;并行流需谨慎,小数据量反而更慢,且严禁有副作用操作。

Java 的 Stream 不是数据容器,也不替代集合;它是一次性、不可重用的计算管道——用完即弃,重复使用会抛 IllegalStateException

Stream 是惰性求值的,不触发终端操作就不会执行中间操作

写完 filtermap 这些中间操作,什么都不会发生。只有调用 collect()forEach()count() 等终端操作时,整个链才真正运行。

  • 常见错误:只写 list.stream().filter(x -> x > 0).map(String::valueOf) 却没接终端操作,结果无任何输出、无异常、也无实际计算
  • 调试技巧:在 mapfilter 中临时加 peek(System.out::println),能确认是否被触发
  • 性能影响:惰性让多步转换可合并优化(如连续两个 filter 可能被 JVM 内联),但过度嵌套仍可能拖慢可读性

parallelStream() 并不总是更快,且有副作用风险

并行流底层用的是 ForkJoinPool.commonPool(),默认线程数 = CPU 核心数 − 1。小数据量或操作本身很轻(比如 x + 1),并行反而因分片、合并、线程调度而更慢。

  • 适用场景:集合大小 > 10⁴ 且每个元素处理耗时明显(如 IO、复杂计算)
  • 严禁在 forEach 中修改外部变量或共享状态,例如:
    int[] count = {

    0}; stream.parallelStream().forEach(x -> count[0]++); // 结果不确定
  • 安全替代:用 mapToInt + sum()collect(Collectors.groupingByConcurrent()) 等无状态聚合方式

Stream 不能复用,也不能从 Stream 反向获取原始集合

Stream 是消费型资源。一旦调用过 collect()count(),再调用任何操作都会立即抛出 IllegalStateException: stream has already been operated upon or closed

立即学习“Java免费学习笔记(深入)”;

  • 典型翻车点:把 stream 赋给变量后反复用,例如:
    Stream s = list.stream().map(String::toUpperCase); s.count(); s.collect(Collectors.toList()); // 这里报错
  • 正确做法:要么链式写到底(推荐),要么每次需要都重新生成 stream:list.stream().filter(...).collect(...)
  • 注意:没有 stream.toCollection()stream.source() 这类方法——Stream 设计上就拒绝暴露源头

最常被忽略的一点:Stream 操作的「无状态」要求不是建议,而是契约。哪怕一个 map 里偷偷改了某个 static 计数器,就可能在并行时引发不可预测行为——这不是 bug,是违反模型前提的误用。