惰性集合操作:序列
map 和 filter 函数在集合上执行时,会及早创建中间集合。这意味着每一步的中间结果都被存储在一个临时列表中。例如,先 filter 再 map,会先生成一个过滤后的列表,再生成映射后的列表。当元素很多时,这种方式非常低效,占用额外内存且增加 GC 压力。
序列(Sequence)给了你执行这些操作的另一种选择,可以避免创建这些临时中间对象。

filter 和 map 都会返回一个列表,这意味着上图的链式调用会创建两个列表:一个保存 filter 函数的结果,一个保存 map 的结果。而序列则不同。

上图没有创建任何用于存储元素的中间集合。Kotlin 惰性集合操作的入口就是 Sequence 接口,这个接口表示一个可以逐个列举元素的元素序列,它只提供了一个方法 iterator,用来从序列中获取值。这个接口的强大之处在于其操作的实现方式,序列中的元素求值是惰性的。用序列可以更高效地执行链式操作,而不用创建额外的集合来保存过程中产生的中间结果。
可以调用扩展函数 asSequence 把任意集合转换成序列,调用 toList 来做反向的转换。为什么要把序列转换回集合?如果只需要迭代序列中的元素,可以直接使用序列。如果要使用其他 API 方法,比如用下标访问元素,那么就需要把序列转换成列表。
执行序列操作:中间和末端操作
序列操作分为两类:中间的和末端的。一次中间操作返回的是另一个序列,这个新序列知道如何变换原始序列中的元素。而一次末端操作返回的是一个结果,这个结果可能是集合、元素、数字,或者其他从初始集合的变换序列中获取的任意对象。
在上面的图中我们可以看出 map 和 filter 函数是中间操作,而 toList 是末端操作。上方的例子去掉 toList 后,它们不会进行任何操作,因为 map 和 filter 函数的变换被延期了,他们只有在获取结果的时候才会被调用。只有末端操作会触发执行所有的延期计算。
对序列来说,所有操作是按顺序应用在每一个元素上:处理完第一个元素(先映射再过滤),然后完成第二个元素的处理,以此类推。这种方法意味着如果在轮到它们之前就已经取得了结果,那部分元素根本不会发生任何变换。例子:一个 map 加上一个 find,当 find 找到符合条件的元素后,后面的元素无需继续完成操作。而同样的操作应用在集合上时,那么 map 的结果首先被求出来,然后满足判断式的一个元素会被找出来。惰性方法意味着你可以跳过处理部分元素。
在集合上执行操作的顺序也会影响性能,filter 和 map 之间,先应用 filter 有助于减少变换的总次数。
性能考量与适用场景
虽然序列提供了惰性求值的优势,但并非在所有场景下都优于集合。对于小型集合,创建 Iterator 对象的开销可能超过节省的内存收益。此外,序列不支持随机访问(如通过索引获取元素),也不支持某些需要多次遍历的操作。因此,建议在处理大数据流、复杂链式过滤或仅需单次遍历的场景下优先使用序列。
创建序列
- 在集合上调用
asSequence generateSequence给定序列中的前一个元素,这个函数会计算出下一个元素
生成并使用自然数序列(计算 100 以内所有自然数之和)
val sum = generateSequence(1) { it + 1 }
.takeWhile { it <= }
.sum()
println(sum)






