前幾節中,你看到了不少鏈式集合函數調用的樣子,好比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
複製代碼