在 Kotlin 中,Sequence (序列)是一种用于高效处理集合数据的惰性容器。

与普通集合(如 ListSet)不同,Sequence 在处理链式操作时避免了中间集合的多次创建,从而显著减少内存开销并提升性能。

1 惰性求值

Kotlin 的集合操作(如 mapfilter)默认是急切的,即每一步操作都会立即执行并生成中间结果:

listOf(1, 2, 3, 4, 5)
    .filter {
        println("filter $it")
        it % 2 == 0
    }
    .map {
        println("map $it")
        it * 2
    }
    .toList()

// filter 1 ——> filter 2 ——> filter 3 ——> filter 4 ——> filter 5
// map 2 ——> map 4

每一步操作生成中间集合,遍历所有元素,内存开销大。

Sequence 的操作是惰性的,不会立即执行中间操作,直到在调用终端操作(如 toListforEach)时才会执行整个操作链。

listOf(1, 2, 3, 4, 5)
    .asSequence()
    .filter {
        println("filter $it")
        it % 2 == 0
    }
    .map {
        println("map $it")
        it * 2
    }
    .toList()

// filter 1 ——> filter 2 ——> map 2 ——> filter 3 ——> filter 4 ——> map 4 ——> filter 5

逐个元素处理,没有中间集合生成,内存占用低。

2 高效性的核心原因

2.1 减少中间集合的创建
  • 普通集合:每次链式操作(如 filter -> map -> take)都会生成新的中间集合,导致内存占用与元素数量成线性增长;
  • Sequence:所有操作合并为一个“处理链”,逐个元素依次执行所有操作,无中间集合生成;
  • Sequence 在处理复杂操作链时通常只需要一次遍历,而传统集合需要多次遍历;
2.2 提前终止处理

当使用 take(n)first() 等操作时,Sequence 会在满足条件后立即终止处理,避免不必要的计算:

(1..1_000_000)
    .asSequence()
    .filter { it % 2 == 0 }
    .map { it * 2 }
    .take(3) // 只处理到找到 3 个元素为止
    .toList()

普通集合会先处理全部的 100 万元素,而 Sequence 可能仅处理到第 6 个元素(找到 3 个偶数后停止)。

3 创建 Sequence 的方式

3.1 从现有集合转换
val list = listOf(1, 2, 3)
val sequence = list.asSequence()
3.2 使用 sequenceOf 函数
val sequence = sequenceOf(1, 2, 3, 4)
3.3 使用 sequence 构建器
val sequence = sequence {
    yield(1) // 生成单个元素
    yieldAll(listOf(2, 3)) // 生成多个元素
    yieldAll(generateSequence(4) { it + 1 }) // 生成无限序列(需配合 take 使用)
}

println(sequence.take(5).toList()) // [1, 2, 3, 4, 5]
3.4 使用生成器创建
val numbers = generateSequence(1) { it + 1 } // 无限序列
val first10 = numbers.take(10).toList() // [1, 2, 3, ..., 10]

4 适用场景

  • 大规模数据处理: 当需要处理大量数据时,Sequence 可以显著减少内存;
  • 复杂操作链: 当操作链包含多个步骤时,Sequence 可以避免创建中间集合;
  • 无限序列: Sequence 适合表示无限数据流,如随机数生成、斐波那契数列等;
  • 需要提前终止处理:takefirst

5 注意事项

  • 终端操作才会执行整个操作链: 如果不调用终端操作(如 toListforEach),则不会执行任何操作;
  • Sequence 通常是一次性的: 终端操作后不能再次使用,避免重复使用同一 Sequence
  • 不缓存结果: Sequence 每次遍历都会重新计算,若需复用结果,应先转换为集合(如 toList());
  • 不适合随机访问: Sequence 本质是单向遍历的数据流,不支持 get(index) 操作;
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐