Stream核心概念

这里是把一些下一章源码中可能涉及的晦涩术语介绍一下。

# 操作属性

# HEAD & 中间操作 & TERMINAL

stream多个操作以链表的方式组织在一起,自然会有头结点、中间节点、尾节点的概念。

  • HEAD:通过创建 stream (如 Stream.of(...)、list.stream()...)开启流操作时,注册的节点。
    不具备操作内容,仅是记录初始 spiliterator、流标识。
  • 中间操作:是那些返回值依然是 Stream 的实际操作(如 filter、map、sorted...),可以继续追加中间操作/TERMINAL。
    中间操作是惰性执行的,即添加了中间操作后不会立即执行该操作,仅是一个操作内容的注册行为,会在 TERMINAL 操作时开始执行。
  • TERMINAL:也叫终结操作,是返回值非 Stream 的实际操作(如 forEach、collect...),注册一个终结操作代表要收集某些数据,也意味着流执行完毕。

# 有 & 无状态操作

是流的一个属性,对应两个子类:StatelessOp、StatefulOp,实现了 AbstractPipeline.opIsStateful 方法

  • 有状态操作
    • 定义:需要有内部状态(中间值...)来累计结果。
    • 特点:
      • 需要维护额外的状态信息,线程不安全。
      • 在并行流中,流操作链被分成了多段 [HEAD/有状态操作的输出,下一个有状态操作的执行/FINALLY],也就是说有状态操作必须保证前置数据全部拿到。每一阶段都是分为两步,1.本段开头->有状态操作执行前,2.有状态操作执行
    • 例:
      • sorted:对前面所有元素排序(状态:前面所有元素)
      • max:获取前面所有元素的最大值(状态:已通过元素的最大值)
      • limit:计数短路前n个元素(状态:已通过元素数量)
  • 无状态操作
    • 定义:每个元素的处理相互独立
    • 特点:可以并行处理,性能更好,线程安全
    • 例:
      • filter:独立判断每个元素
      • map:独立转换每个元素

关于“有状态操作”的一个常见误区

网上很多文章提到“有状态操作需要收集前面所有数据才能进行下一步”,但往往忽略了一个关键前提:这通常只在并行流(Parallel Stream)中成立。

从源码层面看,二者的执行逻辑差异巨大:

  • 并行流:会将操作链切分为多个阶段,必须等待当前阶段的有状态操作处理完所有数据,才能进入下一阶段。
  • 串行流:“流水线”式的。数据从上游流下,经过中间操作(即便是有状态的)处理后,会立即传递给下游。

唯一的特例是 sorted()​。在串行流中,只有排序操作是真正的“全量阻断”——因为它必须在 accept() 阶段拿到所有数据并在 end() 完成排序后,才能再次向下游发送数据。而像 limit 或 distinct 这样的操作,在串行流中并不需要等待所有数据到齐。 口说无凭,代码为证。可以试试下面这段代码,limit 和 distinct 都是有状态操作,但它们并没有阻塞数据的流动:

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
        .filter(x -> {
            System.out.println("filter1 x=" + x);
            return true;
        })
        .limit(4)
        .map(x -> {
            System.out.println("map1 x=" + x);
            return x;
        })
        .map(x -> {
            System.out.println("map2 x=" + x);
            return x;
        })
        .distinct()
        .forEach(x -> {
            System.out.println("forEach x=" + x);
        });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

若是上面这段内容看不明白的话,也不用着急,后两章节会从源码的角度分析这个问题,这里我的观点仅是:串行流中,有状态操作并不与“全量阻断收集上游数据”的行为挂钩,“有状态”仅代表有需要额外维护的信息。
如果觉得我说的不对的也可以在评论区敲我或者在主页加我聊,欢迎指正~

# 特性标识

  • DISTINCT:已去重
  • SORTED:已自然排序
  • ORDERED:遭遇顺序是确定的
  • SIZED:数量确定
  • SHORT_CIRCUIT:是短路操作(无需处理全部元素就能返回结果的操作,如 limit、anyMatch、allMatch...)

具体内部的传递逻辑本文不提,这里涉及到操作权限验证(当前是HEAD、中间操作还是终结操作,能不能设置当前标识...)、掩码和位运算结合的设置方式,逻辑晦涩但是核心逻辑都在 StreamOpFlag 枚举类中了,可以自己选择性阅读。

特性标识存在的意义是在于优化逻辑,比如:

  • 前面已经打了 DISTINCT 标识,后续遇到 distinct() 操作时便无需执行。
    set.stream().distinct()
  • 前面已经打了 SORTED 标识,说明已排好序了,可以使用更简单的方式去重(按顺序遍历,当前元素不等于上一个元素即可通过)。
    list.stream().sort().distinct()

# Spliterator

类似于迭代器,是用于对源进行遍历的。但是 Spliterator 支持把自己拆成出多个 Spliterator,以支持并行遍历。

Last Updated: 2/10/2026, 8:57:40 PM