无状态操作(如filter、map)独立处理各元素,无需依赖历史数据,内存占用小、可惰性执行且易并行;有状态操作(如sorted、distinct)需缓存数据、等待全部输入,内存开销大、延迟执行受限、并行效率低。

Stream中间操作的无状态与有状态特性,直接决定变量处理时的内存占用、执行时机和并行效率。关键不在“用了什么方法”,而在于“这个方法是否需要记住前面的数据”。
看操作是否依赖历史数据
判断一个中间操作是否有状态,最直接的方式是问:处理当前元素时,需不需要知道之前处理过哪些元素?
-
无状态操作:filter、map、peek、flatMap —— 每个元素独立处理,输入一个,输出一个,不查“流水账”。比如用
filter(x -> x > 10)筛数,对每个数字只比一次,无需缓存其他数字。 -
有状态操作:sorted、distinct、limit、skip —— 必须先看到全部(或足够多)元素才能开始输出。例如
distinct()得把已出现的值全记下来;sorted()得收集完再排序,无法边读边发结果。
性能差异体现在三方面
无状态和有状态不是语法区别,而是运行机制的根本不同,影响实际执行效果:
- 内存开销:有状态操作常需缓存全部或部分数据。distinct在最坏情况下要存所有唯一值;sorted默认使用Timsort,空间复杂度O(n)。大数据量下容易触发GC甚至OOM。
- 延迟执行受限:无状态操作可完全惰性化,配合短路终止操作(如findFirst、anyMatch)能提前结束;但有状态操作如sorted必须等流结束才开始排序,短路失效。
- 并行处理难度:无状态操作天然适合分割(Spliterator可任意切分),map/filter并行流效率接近线性提升;而sorted/distinct需全局协调,parallelStream中反而可能因合并开销抵消收益。
实践中怎么选和怎么调
不是禁用有状态操作,而是清楚代价,做合理安排:
- 优先把filter/map放在sorted/distinct之前,大幅减少后续有状态操作的数据量。例如先
filter(status::isPaid).distinct(),别等去重时还带着几千条“待支付”记录。 - 避免链式多次有状态操作,如
.sorted().distinct().sorted()——第二次sorted毫无必要,且重复消耗。 - 大数据场景下,考虑用外部工具替代:去重用HashSet预处理,排序用数据库ORDER BY或专门索引,比Stream.sorted更可控。
- 调试时可用
peek(System.out::println)观察执行顺序,验证是否真惰性——你会发现有状态操作前的peek总在它之后才打印,说明数据确实被暂存了。
本质是让数据流动方式匹配业务逻辑节奏:无状态像传送带,随来随走;有状态像装配站,得等人齐了才开工。选对节奏,性能问题就解决了一大半。



















