Kotlin入門筆記八:Lambda和序列

kotlin

序列(Sequence):

首先我們來了解一下什麼是序列,序列其實類似集合的一個接口,只不過它的操作都是惰性集合操作,所有在集合上的操作符都適用於序列。下面我們來看看如何生成一個序列:

// 列表和序列
val list = listOf(1,2,3,4)
println(list)
println(list.asSequence())

輸出:
[1, 2, 3, 4]
kotlin.collections.CollectionsKt___CollectionsKt$asSequence$$inlined$Sequence$1@13fee20c

生成一個序列的一種方式就是通過list.asSequence(),但是我麼在輸出日誌中才發現它的輸出其實沒有內容,那麼如果要查看序列的內容可以將它轉換爲集合。

println(list.asSequence().toList())

輸出:
[1, 2, 3, 4]

這樣我們接可以看到結果。


序列的優勢:

那麼有了集合這麼好用的東西,我們爲什麼要使用序列呢?接下來我們看一個小小的例子:
我們拿到了100W條用戶數據,需要將這些用戶年齡是偶數的姓名全打印出來。如果不使用序列,我們的做法爲:

println(userList
        .filter { it.age % 2 != 0 }
        .map { User::age })

這個寫法肯定沒有錯,對於少量數據來說也沒什麼影響,可是這裏大家注意,是100W條數據,我們可以點擊mapfilter查看源碼,每一步操作都會生成另外一個集合,對於大量數據來說,這可是一筆很大的消耗,在性能上是很不理想的。這時候就到了我們序列大顯身手的時候了,來看看序列是如何減少性能消耗的:

println(userList.asSequence()
        .filter { it.age % 2 != 0 }
        .map { User::age }
        .toList())

這裏我們先將集合轉換爲序列,在最後的操作中才將序列轉換爲集合的。序列在中間操作都是惰性的,不會創建額外的集合來保存過程中產生的中間結果,使用序列可以高效的對集合元素執行鏈式操作。


序列操作的執行過程:

序列操作.png

序列操作分爲兩個過程:中間操作、末端操作。中間操作全部都是惰性操作,如果沒有執行末端操作,中間操作都會被延期。我們來看以下代碼加深理解:

val list = listOf(1, 2, 3, 4)
list.asSequence().filter { print(it);it > 2 }
list.asSequence().filter { print(it);it > 2 }.toList()

我們在中間操作filter()打印集合的元素,但是在日誌中會發現,不加toList()的是不會有任何結果的,只有加上了toList()纔會執行print(it)操作,也就證明了爲什麼說中間操作是惰性的,在沒有末端操作的時候,中間操作會被延期。

注意:末端操作的定義可以理解爲:只要不是在這個操作後生成的對象依舊是序列就屬於末端操作。


序列和集合的執行順序:

對於一個鏈式操作,我們可以先大致猜一下,序列和集合的執行順序是否是一樣?
我們來通過一個簡單的例子理解下:

    val list = listOf(1, 2, 3, 4)
    list.map { print("map($it) "); it * it }
            .filter { print("filter($it )"); it > 5 }
    println()
    list.asSequence()
            .map { print("map($it) ");it * it }
            .filter { print("filter($it) ");it > 5 }
            .toList()

這個例子很簡單,map之後filter,每一步操作我們都做輸出處理,結果如下:

map(1) map(2) map(3) map(4) filter(1 )filter(4 )filter(9 )filter(16 )
map(1) filter(1) map(2) filter(4) map(3) filter(9) map(4) filter(16) 

從結果我們一眼就能看出,集合和序列在鏈式的執行順序是不一樣的,集合在鏈式中先處理完第一步所有的元素,再處理第二步所有的元素,以此類推。而序列不是,序列是對每個元素做鏈式操作,只有第一個元素執行完所有的鏈式操作,才執行第二個元素的鏈式操作。

千萬不要小瞧這個區別,它將爲我們在大量數據處理上帶來很大的優化和便捷。比如:

    val list = listOf(1, 2, 3, 4,·······)
    list.map { it * it }
            .find { it > 5 }
    list.asSequence()
            .map { it * it }
            .find { it > 5 }

這裏我們想象集合中有大量的數據,集合和序列的執行流程如下:

序列和集合的執行順序.png

從圖中我們可以得出這樣一個結論,在map()中間操作的時候,集合需要將每一個元素都執行一次,但是序列在找到滿足find { it > 5 }的元素時,map()操作將不會繼續執行,可以減少不需要的大量操作。這就是序列的另一大優勢。


總結:

無論是在集合還是序列的鏈式操作中,都需要大量的使用到Lambda,熟練的使用Lambda結合集合和序列的轉換將在日常開發中得到一種很完美的編碼體驗,希望大家一定要熟悉的理解序列和集合之間聯繫和區別。

下一份筆記將爲大家帶來Kotlin版的MVP+OKhttp3+Retrofit+RxJava的項目基本框架


寫在最後

每個人不是天生就強大,你若不努力,如何證明自己,加油!

Thank You!

--Taonce

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章