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

Java 的 Stream 不是数据容器,也不替代集合;它是一次性、不可重用的计算管道——用完即弃,重复使用会抛 IllegalStateException。
Stream 是惰性求值的,不触发终端操作就不会执行中间操作
写完 filter、map 这些中间操作,什么都不会发生。只有调用 collect()、forEach()、count() 等终端操作时,整个链才真正运行。
- 常见错误:只写
list.stream().filter(x -> x > 0).map(String::valueOf)却没接终端操作,结果无任何输出、无异常、也无实际计算 - 调试技巧:在
map或filter中临时加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,是违反模型前提的误用。










