序列

    Iterable 的处理包含多个步骤时,它们会优先执行:每个处理步骤完成并返回其结果——中间集合。 在此集合上执行以下步骤。反过来,序列的多步处理在可能的情况下会延迟执行:仅当请求整个处理链的结果时才进行实际计算。

    操作执行的顺序也不同:Sequence 对每个元素逐个执行所有处理步骤。 反过来,Iterable 完成整个集合的每个步骤,然后进行下一步。

    因此,这些序列可避免生成中间步骤的结果,从而提高了整个集合处理链的性能。 但是,序列的延迟性质增加了一些开销,这些开销在处理较小的集合或进行更简单的计算时可能很重要。 因此,应该同时考虑使用 SequenceIterable,并确定在哪种情况更适合。

    要创建一个序列,请调用 函数,列出元素作为其参数。

    Iterable

    如果已经有一个 Iterable 对象(例如 ListSet),则可以通过调用 asSequence() 从而创建一个序列。

    1. val numbersSequence = numbers.asSequence()

    创建序列的另一种方法是通过使用计算其元素的函数来构建序列。 要基于函数构建序列,请以该函数作为参数调用 。 (可选)可以将第一个元素指定为显式值或函数调用的结果。 当提供的函数返回 null 时,序列生成停止。因此,以下示例中的序列是无限的。

    1. fun main() {
    2. val oddNumbersLessThan10 = generateSequence(1) { if (it + 2 < 10) it + 2 else null }
    3. println(oddNumbersLessThan10.count())
    4. //sampleEnd
    5. }

    由组块

    最后,有一个函数可以逐个或按任意大小的组块生成序列元素——sequence() 函数。 此函数采用一个 lambda 表达式,其中包含 与 yieldAll() 函数的调用。 它们将一个元素返回给序列使用者,并暂停 sequence() 的执行,直到使用者请求下一个元素。 yield() 使用单个元素作为参数;yieldAll() 中可以采用 Iterable 对象、Iterable 或其他 SequenceyieldAll()Sequence 参数可以是无限的。 当然,这样的调用必须是最后一个:之后的所有调用都永远不会执行。

    关于序列操作,根据其状态要求可以分为以下几类:

    • 无状态 操作不需要状态,并且可以独立处理每个元素,例如 或 filter()。 无状态操作还可能需要少量常数个状态来处理元素,例如 。
    • 有状态 操作需要大量状态,通常与序列中元素的数量成比例。

    如果序列操作返回延迟生成的另一个序列,则称为 中间序列。 否则,该操作为 末端 操作。 末端操作的示例为 toList() 或 。只能通过末端操作才能检索序列元素。

    序列可以多次迭代;但是,某些序列实现可能会约束自己仅迭代一次。其文档中特别提到了这一点。

    我们通过一个示例来看 IterableSequence 之间的区别。

    假定有一个单词列表。下面的代码过滤长于三个字符的单词,并打印前四个单词的长度。

    1. //sampleStart
    2. val words = "The quick brown fox jumps over the lazy dog".split(" ")
    3. val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
    4. .map { println("length: ${it.length}"); it.length }
    5. .take(4)
    6. println("Lengths of first 4 words longer than 3 chars:")
    7. println(lengthsList)
    8. //sampleEnd

    Sequence

    现在用序列写相同的逻辑:

    此代码的输出表明,仅在构建结果列表时才调用 filter()map() 函数。 因此,首先看到文本 “Lengths of..” 的行,然后开始进行序列处理。 请注意,对于过滤后剩余的元素,映射在过滤下一个元素之前执行。 当结果大小达到 4 时,处理将停止,因为它是 可以返回的最大大小。

    序列处理如下图:

    Sequences processing

    在此示例中,序列处理需要 18 个步骤,而不是 23 个步骤来执行列表操作。