Java Stream是一次性不可重用的计算管道,消费后抛IllegalStateException;需每次重新创建或转为集合复用,parallelStream慎用于小数据或含I/O场景,collect操作需注意null与线程安全问题。
Java 的 Stream 不是集合,也不是数据结构,它是一次性、不可重用的计算管道——用错一次,后续调用 forEach、collect 就会抛 IllegalStateException: stream has already been operated upon or closed。
很多人把 Stream 当成 List 那样反复遍历,结果在第二次 collect() 或 count() 时直接报错。这不是 bug,是设计使然:中间操作(如 filter、map)不执行,终端操作(如 collect、findFirst、forEach)才触发流水线并消耗流。
Streamstream = list.stream().filter(s -> s.length() > 3); List a = stream.collect(Collectors.toList()); List b = stream.collect(Collectors.toList()); // 报 IllegalStateException
list.stream();或先转为集合再复用:List filtered = list.stream().filter(...).collect(Collectors.toList());
Stream ——该用 Collection 或数组parallelStream() 底层用的是 ForkJoinPool.commonPool(),启动开销、任务拆分、结果合并都有成本。对几千以内元素的简单 CPU 计算,串行反而更稳更快;若操作中含同步块、System.out.println、文件读写等阻塞行为,还可能引发线程竞争或死锁。
synchronized、ThreadLocal、数据库连接、日志输出、Random 实例(非线程安全)stream.parallel().sequential() 强制切回串行调试,但注意这不重置流状态collect() 是最易出错的终端操作之一,尤其混淆 Collectors.toList() 和 Collectors.toCollection(ArrayList::new) 的语义差异,或误用 toMap 导致 NullPointerException。
Collectors.toList() 返回的是不可保证具体类型的 List(JDK 16+ 是 ArrayList,但规范不保证),且不保证线程安全;并发流中用它可能产生未定义行为toMap(keyMapper, valueMapper) 要求 key 不能为 null,否则抛 NullPointerException;遇到重复 key 默认失败,需显式传第三个参数 BinaryOperator 处理冲突(如 (a,b) -> a)LinkedHashSet 去重保序?别用 toSet()(不保序),改用:stream.collect(Collectors.toCollection(LinkedHashSet::new))
当 map 中返回 null,再接 filter(Objects::nonNull) 看似能兜底,但其实没用——map 本身不会过滤,它只是把元素转成 null,而后续 filter 才负责筛掉这些 null。真正危险的是:某些自定义 map 函数未判空,导致 NullPointerException 直接中断流。
list.stream()
.filter(Objects::nonNull)
.map(s -> s.toUpperCase(Locale.ROOT))
.filter(Objects::nonNull)Optional 包装映射逻辑,或用 mapMulti(JDK 16+)替代复杂转换flatMap 返回空 Stream.empty() 是合法的,等价于“跳过该元素”,不是 null
Stream 的核心价值不在语法糖,而在声明式表达“做什么”,而非“怎么做”。但它的不可变性、一次性、延
