Java Stream 是一次性管道,执行终端操作后即关闭,重复调用会抛 IllegalStateException;filter 保留元素、map 一对一转换、flatMap 展平嵌套流;需过滤 null 避免 NPE;并行流非万能,受限于数据量、操作状态及 IO 类型。

Stream 是一次性消费的,不能重复调用 collect() 或 forEach()
Java 的 Stream 不是数据容器,而是一次性管道。一旦执行了终端操作(如 collect()、forEach()、count()),流就关闭了。再调用会抛出 java.lang.IllegalStateException: stream has already been operated upon or closed。
- 错误写法:
List
list = Arrays.asList("a", "b"); Stream s = list.stream(); s.collect(Collectors.toList()); s.forEach(System.out::println); // ❌ 报错 - 正确做法:每次需要新流就重新创建 ——
list.stream().map(...).collect(...),或把中间操作链写完整再执行终端操作 - 调试时别在中间插
forEach()打断流;要用peek():stream.peek(System.out::println).filter(...)
filter()、map()、flatMap() 的关键区别
这三个是最常混用的中间操作,核心差异在「返回值类型」和「是否扁平化」:
-
filter(Predicate:输入) T,输出仍是T,但只保留满足条件的元素(数量 ≤ 原始) -
map(Function:输入) T,输出R,一对一转换(数量不变) -
flatMap(Function:输入>) T,输出Stream,然后自动“展平”一层(适合处理嵌套集合、Optional、字符串转字符流等)
例如把 List 拉平成 >
List,必须用 flatMap:
listOfLists.stream()
.flatMap(innerList -> innerList.stream())
.collect(Collectors.toList());
空集合、null 元素与 Optional 的配合要点
Stream 本身不拒绝 null 元素,但多数终端操作(如 collect())在遇到 null 时可能抛 NullPointerException,尤其在使用 Collectors.toMap() 时键或值为 null 会直接失败。
立即学习“Java免费学习笔记(深入)”;
- 安全过滤 null:
stream.filter(Objects::nonNull) - 避免
toMap()因 key 为 null 失败:stream.filter(Objects::nonNull) .collect(Collectors.toMap( item -> item.getId(), item -> item.getName(), (a, b) -> a // 冲突解决,防止 key 重复 )); - 对可能为空的计算结果,优先用
Optional包裹再进流:Optional.ofNullable(obj).map(...).stream(),比在流里判空更清晰
并行流(parallelStream())不是性能银弹
parallelStream() 底层用的是 ForkJoinPool.commonPool(),看似开箱即用,但实际受多个因素制约:
- 数据量小(比如 计算收益
- 操作有状态(如修改外部变量、使用非线程安全的
ArrayList收集)会导致结果不可预测,必须改用线程安全容器或collect()的三参数重载 - IO 密集型操作(如读文件、发 HTTP 请求)不适合并行流,应考虑
CompletableFuture等异步模型 - 想控制线程池?别用
commonPool(),显式构造ForkJoinPool并用submit()提交任务更可控
真正需要并行时,优先验证单线程瓶颈、确认操作无状态、再压测对比。










