Kotlin 中的 Collections and Sequences 對比記錄

Kotlin 中 Collections(集合)是非常好用的數據結構,同時 Kotlin 的標準庫中也實現了非常多的擴展方法,幫助我們更好的使用 Collections;我們發現kotlin還有一個數據結構Sequences,那麼它倆到底與什麼區別呢?我也是把很多家的資料進行總結和學習簡單的來記錄一下!

首先Sequences到底是什麼?

Sequences在kotlin裏面叫序列,它的一系列操作叫惰性集合操作,Sequences序列接口強大在於其操作的實現方式,序列中的元素求值都是惰性的,所以可以更加高效使用序列來對數據集中的元素進行鏈式操作(映射、過濾、變換等),而不需要像普通集合那樣,每進行一次數據操作,都必須要開闢新的內存來存儲中間結果,而實際上絕大多數的數據集合操作的需求關注點在於最後的結果而不是中間的過程。

那麼中間過程到底是指的什麼呢?

就是我們每一次對集合進行的轉換操作,普通的集合是每一種變化全部執行完畢之後再進行新的集合創建然後再進行新的變換,而序列在中間過程每一個元素進行操作之後立即會進行下一次中間操作,會生產中間對象進行保存,而不會生產新的集合對象,所以對於內存的開銷是最小的,所以這也說明了它是惰性的集合。

什麼叫末端操作呢?

末端操作是序列執行之後最後返回的結果可以是集合、數字、或者從其他對象集合變換得到任意對象,中間操作會直接返回sequences對象,然後進行下一次的變換操作(中間操作)。

說道現在怎麼使用的?

其實使用起來很簡單,主要是理解和運用的時機我們大家要掌握好。

    //定義聲明
    public fun <T> Iterable<T>.asSequence(): Sequence<T> {
        return Sequence { this.iterator() }
    }
    //調用實現
    list.asSequence()

會發現我們使用的時候只要在list集合之上使用asSequence的擴展方法即可,立即就會把普通集合轉變成序列,使用的時候性能就會立馬得到提升。

(0..10000000)//10000000數據量級
.asSequence()
.map { it + 1 }
.filter { it % 2 == 0 }
.count { it < 100 }
.run {
    println("by using sequence result is $this")
}

我們發現集合會特別大,那這個時候我們使用序列就會在性能方面得到很大的提升,因爲我們各種中間操作之後創建一個集合,不會有額外的消耗。

總結如下:在數據量級比較大情況下使用Sequences序列性能會比普通數據集合更優;但是在數據量級比較小情況下使用Sequences序列性能反而會比普通數據集合更差。

兩種幾個的原理如下:

序列操作: 基本原理是惰性求值,也就是說在進行中間操作的時候,是不會產生中間數據結果的,只有等到進行末端操作的時候纔會進行求值。也就是上述例子中0~10中的每個數據元素都是先執行map操作,接着馬上執行filter操作。然後下一個元素也是先執行map操作,接着馬上執行filter操作。然而普通集合是所有元素都完執行map後的數據存起來,然後從存儲數據集中又所有的元素執行filter操作存起來的原理。

集合普通操作: 針對每一次操作都會產生新的中間結果,也就是上述例子中的map操作完後會把原始數據集循環遍歷一次得到最新的數據集存放在新的集合中,然後進行filter操作,遍歷上一次map新集合中數據元素,最後得到最新的數據集又存在一個新的集合中。

再舉一個例子,會比較好說明這個問題:

//使用序列
fun main(args: Array<String>){
    (0..100)
            .asSequence()
            .map { it + 1 }
            .filter { it % 2 == 0 }
            .find { it > 3 }
}
//使用普通集合
fun main(args: Array<String>){
    (0..100)
            .map { it + 1 }
            .filter { it % 2 == 0 }
            .find { it > 3 }
}

我們知道find函數是隻要找到符合條件的數就會立馬返回true,根據我們瞭解的序列的特性,我們不難發現,序列在每一個元素進行map、filter、find等操作,只要發現符合find裏面條件的就會終止立馬返回結果,如果是傳統的集合那我們就會發現它會所有的操作都執行完畢之後,最後發現find符合條件之後纔會返回結果,中間會多做很多無用功,這也就是序列的精髓所在。

最後總結一下原理:

序列內部的實現原理是採用狀態設計模式,根據不同的操作符的擴展函數,實例化對應的Sequence子類對象,每個子類對象重寫了Sequence接口中的iterator()抽象方法,內部實現根據傳入的迭代器對象中的數據元素,加以變換、過濾、合併等操作,返回一個新的迭代器對象。這就能解釋爲什麼序列中工作原理是逐個元素執行不同的操作,而不是像普通集合所有元素先執行A操作,再所有元素執行B操作。這是因爲序列內部始終維護着一個迭代器,當一個元素被迭代的時候,就需要依次執行A,B,C各個操作後,如果此時沒有末端操作,那麼值將會存儲在C的迭代器中,依次執行,等待原始集合中共享的數據被迭代完畢,或者不滿足某些條件終止迭代,最後取出C迭代器中的數據即可。

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