Skip to content

序列性能优化 (Sequences)

源:Kotlin 序列官方文档

Kotlin 的 Sequence 类似于 Java 8 的 Stream。它采用 惰性求值 (Lazy Evaluation) 策略,显著区别于 Iterable (List/Set) 的 及早求值 (Eager Evaluation)

核心差异:流水线 vs 堆积木

Iterable (List)

每个操作符(map, filter)都会立即执行并创建完整的中间集合。

kotlin
// 假设 list 有 100 万个元素
list.map { it * 2 }      // 步骤 1: 创建一个包含 100 万元素的中间 List
    .filter { it > 10 }  // 步骤 2: 遍历中间 List,再创建最终 List
    .first()             // 步骤 3: 取第一个

缺点:内存开销大,无法处理无限数据流。

Sequence

所有操作符被构建成一个流水线。只有当终端操作符(如 toList, first)被调用时,数据才开始流动。

kotlin
list.asSequence()
    .map { it * 2 }      // 仅注册操作
    .filter { it > 10 }  // 仅注册操作
    .first()             // 开始拉取数据:处理第 1 个 -> map -> filter -> 满足 -> 停止!

优点:几乎无中间内存开销,支持短路优化。

序列构建器:yield

sequence { ... } 构建器允许你通过挂起函数 yield 生成数据。这是一个受限的 协程 环境。

kotlin
val fibonacci = sequence {
    var a = 0
    var b = 1
    
    // 无促生成无限序列,因为 sequence 是惰性的
    // 不会造成死循环
    while (true) {
        yield(a) // 暂停执行,返回值给调用者,直到下一次请求
        val temp = a + b
        a = b
        b = temp
    }
}

// 取前 10 个
println(fibonacci.take(10).toList())

性能基准 (Benchmark)

并不是说所有情况都应该用 Sequence。

数据量操作链长度推荐原因
小 (< 100)任意IterableSequence 自身的对象创建和状态机管理有微小开销,小数据量下得不偿失。
大 (> 1000)多步 (Map+Filter)Sequence避免中间集合创建,显著降低 GC 压力。
无限/流式任意SequenceIterable 根本无法处理无限流。
包含短路任意 (take, first)Sequence避免处理不必要的后续元素。

实战:IO 流处理

利用 Sequence 逐行处理大文件,避免 OOM。

kotlin
// useLines 内部就是由 Sequence 实现的
File("gigantic.log").useLines { lines ->
    lines
        .filter { it.contains("ERROR") } // 只处理错误行
        .map { it.extractErrorCode() }
        .distinct()
        .forEach { report(it) }
}

约束:单次迭代

大部分 Sequence(尤其是基于 Iterator 或 Generator 的)只能被消费一次。如果需要多次遍历,请先 toList() 转为集合。