前几节中,你看到了不少链式集合函数调用的样子,好比map和filter。这些函数会及早地建立中间集合,也就是说每一步的中间结果都被村粗在一个临时列表。markdown
序列给了你执行这些操做的另外一种选择,能够避免建立这些临时中间对象。函数
people.map(Person::name).filter { it.startsWith("A") }
复制代码
Kotlin标准库参考文档有说明,filter
和map
都会返回一个列表。这意味着上面例子中的链式调用会建立两个列表:一个保持filter
函数的结果,另外一个保存map
函数的结果。性能
为了提升效率,能够把操做变成使用序列,而不是直接使用集合:spa
// 把初始集合转换成序列
people.asSequence()
// 序列支持和集合同样的API
.map(Person::name)
.filter { it.startsWith("A") }
//把结果序列转换回序列
.toList()
复制代码
Kotlin惰性集合操做的入口就是***Sequence
接口。这个接口表示的就是一个能够逐个列举元素的元素序列。Sequence
只提供了一个方法,iterator
***,用来从序列中获取值。code
*Sequence
*接口的强大之处在于其操做的实现方法。序列中的元素求值是惰性的。所以,可使用序列更高效地对集合元素执行链式操做,而不须要建立额外的集合来保存过程当中产生的中间结果。orm
先调用扩展函数asSequence把任意集合转换为序列,调用toList来作反向的转换。对象
- 为啥要把序列转换回集合?用序列代替集合不是更方便吗?特别是它们还有这么多优势。
答案:有的时候是这样。若是只须要迭代序列中的元素,能够直接使用序列。若是你要用其余的API方法,好比用下标访问元素,则需将序列转换成列表。接口
序列操做分为两类:中间和末端。一次中间操做返回的是另外一个序列,这个新序列知道如何变换原始序列中的元素。而一次末端操做返回的是一个结果。文档
| ----- 中间操做 ----- |
sequence.map { ... }.filter { ... }.toList()
|-末端操做-|
复制代码
中间操做始终都是惰性的。string
>>> listOf(1,2,3,4).asSequence()
.map { print("map($it)"); it * it }
.filter { print("filter($it)"); it % 2 == 0 }
复制代码
执行这段代码并不会在控制台上输入任何内容。这意味着map和filter变换被延期了,它们只有在获取结果的时候才会被应用。
>>> listOf(1,2,3,4).asSequence()
.map { print("map($it)"); it * it }
.filter { print("filter($it)"); it % 2 == 0 }
.toList()
// 输出结果
map(1) filter(1) map(2) filter(4) map(3) filter(9) map(4) filter(16)
复制代码
这个例子中另一件值得注意的重要事情是计算执行的顺序(其会影响性能)。
建立序列的方式:
asSequence()
generateSequence()
>>> val naturalNumbers = generateSequence(0) { it + 1 }
// numbersTo100是一个序列:[0, 1, 2, ... , 100]
>>> val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
>>> println(numbersTo100.sum())
5050
复制代码