欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin惰性集合操作之Sequence序列使用示例

 更新時間:2023年01月03日 15:03:54   作者:麥田里的守望者江  
這篇文章主要為大家介紹了Kotlin惰性集合操作之Sequence序列使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

集合操作函數(shù) 和 序列

在了解 Kotlin 惰性集合之前,先看一下 Koltin 標(biāo)注庫中的一些集合操作函數(shù)。

定義一個數(shù)據(jù)模型 Person 和 Book 類:

data class Person(val name: String, val age: Int) 
data class Book(val title: String, val authors: List<String>)

filter 和 map 操作:

    val people = listOf<Person>(
            Person("xiaowang", 30),
            Person("xiaozhang", 32),
            Person("xiaoli", 28)
        )
        //大于 30 歲的人的名字集合列表
     people.filter { it.age >= 30 }.map(Person::name)

count 操作:

   val people = listOf<Person>(
            Person("xiaowang", 30),
            Person("xiaozhang", 32),
            Person("xiaoli", 28)
        )
        //小于 30 歲人的個數(shù)
   people.count { it.age < 30 }

flatmap 操作:

      val books = listOf<Book>(
            Book("Java 語言程序設(shè)計", arrayListOf("xiaowang", "xiaozhang")),
            Book("Kotlin 語言程序設(shè)計", arrayListOf("xiaoli", "xiaomao")),
        )
        // 所有書的名字集合列表
        books.flatMap { it.authors }.toList()

在上面這些函數(shù),每做一步操作,都會創(chuàng)建中間集合,也就是每一步的中間結(jié)果都被臨時存儲在一個臨時集合中。

filter 函數(shù)源碼:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    //創(chuàng)建一個新的集合列表
    return filterTo(ArrayList<T>(), predicate)
}
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

map 函數(shù)源碼:

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    //創(chuàng)建一個新的集合列表
    return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
    for (item in this)
        destination.add(transform(item))
    return destination
}

如果被 操作的元素過多,假設(shè) people 或 books 超過 50個、100個,那么 函數(shù)鏈?zhǔn)秸{(diào)用 如:fliter{}.map{} 就會變得低效,且浪費內(nèi)存。

Kotlin 為解決上面這種問題,提供了惰性集合操作 Sequence 接口。這個接口表示一個可以逐個列舉的元素列表。Sequence 只提供了一個 方法, iterator,用來從序列中獲取值。

public interface Sequence<out T> {
    /**
     * Returns an [Iterator] that returns the values from the sequence.
     *
     * Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time.
     */
    public operator fun iterator(): Iterator<T>
}
public inline fun <T> Sequence(crossinline iterator: () -> Iterator<T>): Sequence<T> = object : Sequence<T> {
    override fun iterator(): Iterator<T> = iterator()
}
/**
 * Creates a sequence that returns all elements from this iterator. The sequence is constrained to be iterated only once.
 *
 * @sample samples.collections.Sequences.Building.sequenceFromIterator
 */
public fun <T> Iterator<T>.asSequence(): Sequence<T> = Sequence { this }.constrainOnce()

序列中的元素求值是惰性的。因此,可以使用序列更高效地對集合元素執(zhí)行鏈?zhǔn)讲僮鳎恍枰獎?chuàng)建額外的集合來保存過程中產(chǎn)生的中間結(jié)果。關(guān)于這個惰性是怎么來的,后面再詳細(xì)解釋。

可以調(diào)用擴(kuò)展函數(shù) asSequence 把任意集合轉(zhuǎn)換成序列,調(diào)用 toList 來做反向的轉(zhuǎn)換。

 val people = listOf<Person>(
            Person("xiaowang", 30),
            Person("xiaozhang", 32),
            Person("xiaoli", 28)
        )
  people.asSequence().filter { it.age >= 30 }.map(Person::name).toList()
 val books = listOf<Book>(
            Book("Java 語言程序設(shè)計", arrayListOf("xiaowang", "xiaozhang")),
            Book("Kotlin 語言程序設(shè)計", arrayListOf("xiaoli", "xiaomao")),
        )
 books.asSequence().flatMap { it.authors }.toList()

序列中間和末端操作

序列操作分為兩類:中間的和末端的。一次中間操作返回的是另一個序列,這個新序列知道如何變換原始序列中的元素。而一次末端返回的是一個結(jié)果,這個結(jié)果可能是集合、元素、數(shù)字,或者其他從初始集合的變換序列中獲取的任意對象。

中間操作始終是惰性的。

下面從例子來理解這個惰性

listOf(1, 2, 3, 4).asSequence().map {
            println("map${it}")
            it * it
        }.filter {
            println("filter${it}")
            it % 2 == 0
        }

上面這段代碼在控制臺不會輸出任何內(nèi)容(因為沒有末端操作)。

listOf(1, 2, 3, 4).asSequence().map {
            println("map${it}")
            it * it
        }.filter {
            println("filter${it}")
            it % 2 == 0
        }.toList()
 控制臺輸出:
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: map1
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: filter1
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: map2
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: filter4
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: map3
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: filter9
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: map4
2023-01-01 20:23:05.071 17000-17000/com.wangjiang.example D/TestSequence: filter16

在末端操作 .toList()的時候,mapfilter 變換才被執(zhí)行,而且元素是被逐個執(zhí)行的。并不是所有元素經(jīng)在 map 操作執(zhí)行完成后,再執(zhí)行 filter 操作。

為什么元素是逐個被執(zhí)行,首先看下 toList() 方法:

public fun <T> Sequence<T>.toList(): List<T> {
    return this.toMutableList().optimizeReadOnlyList()
}
public fun <T> Sequence<T>.toMutableList(): MutableList<T> {
    return toCollection(ArrayList<T>())
}
public fun <T, C : MutableCollection<in T>> Sequence<T>.toCollection(destination: C): C {
    for (item in this) {
        destination.add(item)
    }
    return destination
}

最后的 toCollection 方法中的 for (item in this),其實就是調(diào)用 Sequence 中的迭代器 Iterator 進(jìn)行元素迭代。其中這個 this 來自于 filter,也就是使用 filterIterator 進(jìn)行元素迭代。來看下 filter

public fun <T> Sequence<T>.filter(predicate: (T) -> Boolean): Sequence<T> {
    return FilteringSequence(this, true, predicate)
}
internal class FilteringSequence<T>(
    private val sequence: Sequence<T>,
    private val sendWhen: Boolean = true,
    private val predicate: (T) -> Boolean
) : Sequence<T> {
    override fun iterator(): Iterator<T> = object : Iterator<T> {
        val iterator = sequence.iterator()
        var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
        var nextItem: T? = null
        private fun calcNext() {
            while (iterator.hasNext()) {
                val item = iterator.next()
                if (predicate(item) == sendWhen) {
                    nextItem = item
                    nextState = 1
                    return
                }
            }
            nextState = 0
        }
        override fun next(): T {
            if (nextState == -1)
                calcNext()
            if (nextState == 0)
                throw NoSuchElementException()
            val result = nextItem
            nextItem = null
            nextState = -1
            @Suppress("UNCHECKED_CAST")
            return result as T
        }
        override fun hasNext(): Boolean {
            if (nextState == -1)
                calcNext()
            return nextState == 1
        }
    }
}

filter 中又會使用上一個 Sequencesequence.iterator() 進(jìn)行元素迭代。再看下 map

public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {
    return TransformingSequence(this, transform)
}
internal class TransformingSequence<T, R>
constructor(private val sequence: Sequence<T>, private val transformer: (T) -> R) : Sequence<R> {
    override fun iterator(): Iterator<R> = object : Iterator<R> {
        val iterator = sequence.iterator()
        override fun next(): R {
            return transformer(iterator.next())
        }
        override fun hasNext(): Boolean {
            return iterator.hasNext()
        }
    }
    internal fun <E> flatten(iterator: (R) -> Iterator<E>): Sequence<E> {
        return FlatteningSequence<T, R, E>(sequence, transformer, iterator)
    }
}

也是使用上一個 Sequencesequence.iterator() 進(jìn)行元素迭代。所以以此類推,最終會使用轉(zhuǎn)換為 asSequence() 的源 iterator()。

下面自定義一個 Sequence 來驗證上面的猜想:

listOf(1, 2, 3, 4).asSequence().mapToString {
            Log.d("TestSequence","mapToString${it}")
            it.toString()
        }.toList()
    fun <T> Sequence<T>.mapToString(transform: (T) -> String): Sequence<String> {
        return TransformingStringSequence(this, transform)
    }
    class TransformingStringSequence<T>
    constructor(private val sequence: Sequence<T>, private val transformer: (T) -> String) : Sequence<String> {
        override fun iterator(): Iterator<String> = object : Iterator<String> {
            val iterator = sequence.iterator()
            override fun next(): String {
                val next = iterator.next()
                Log.d("TestSequence","next:${next}")
                return transformer(next)
            }
            override fun hasNext(): Boolean {
                return iterator.hasNext()
            }
        }
    }
控制臺輸出:
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: next:1
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: mapToString1
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: next:2
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: mapToString2
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: next:3
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: mapToString3
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: next:4
2023-01-01 20:43:43.899 21797-21797/com.wangjiang.example D/TestSequence: mapToString4

所以這就是 Sequence 為什么在獲取結(jié)果的時候才會被應(yīng)用,也就是末端操作被調(diào)用的時候,才會依次處理每個元素,這也是 被稱為惰性集合操作的原因。

經(jīng)過一系列的 序列操作,每個元素逐個被處理,那么優(yōu)先處理 filter 序列,其實可以減少變換的總次數(shù)。因為每個序列都是使用上一個序列的 sequence.iterator() 進(jìn)行元素迭代。

創(chuàng)建序列

在集合操作上,可以使用集合直接調(diào)用 asSequence() 轉(zhuǎn)換為序列。那么不是集合,有類似集合一樣的變換,該怎么操作呢。

下面以求 1到100 的所有自然數(shù)之和為例子:

val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
val sum = numbersTo100.sum()
println(sum)
控制臺輸出:
5050

先看下 generateSequence 源碼:

public fun <T : Any> generateSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> =
    if (seed == null)
        EmptySequence
    else
        GeneratorSequence({ seed }, nextFunction)
private class GeneratorSequence<T : Any>(private val getInitialValue: () -> T?, private val getNextValue: (T) -> T?) : Sequence<T> {
    override fun iterator(): Iterator<T> = object : Iterator<T> {
        var nextItem: T? = null
        var nextState: Int = -2 // -2 for initial unknown, -1 for next unknown, 0 for done, 1 for continue
        private fun calcNext() {
            //getInitialValue 獲取的到就是 generateSequence 的第一個參數(shù) 0
            //getNextValue 獲取到的就是 generateSequence 的第二個參數(shù) it+1,這個it 就是 nextItem!!
            nextItem = if (nextState == -2) getInitialValue() else getNextValue(nextItem!!)
            nextState = if (nextItem == null) 0 else 1
        }
        override fun next(): T {
            if (nextState < 0)
                calcNext()
            if (nextState == 0)
                throw NoSuchElementException()
            val result = nextItem as T
            // Do not clean nextItem (to avoid keeping reference on yielded instance) -- need to keep state for getNextValue
            nextState = -1
            return result
        }
        override fun hasNext(): Boolean {
            if (nextState < 0)
                calcNext()
            return nextState == 1
        }
    }
}

上面代碼其實就是創(chuàng)建一個 Sequence 接口實現(xiàn)類,并實現(xiàn)它的 iterator 接口方法,返回一個 Iterator 迭代器。

public fun <T> Sequence<T>.takeWhile(predicate: (T) -> Boolean): Sequence<T> {
    return TakeWhileSequence(this, predicate)
}
internal class TakeWhileSequence<T>
constructor(
    private val sequence: Sequence<T>,
    private val predicate: (T) -> Boolean
) : Sequence<T> {
    override fun iterator(): Iterator<T> = object : Iterator<T> {
        val iterator = sequence.iterator()
        var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
        var nextItem: T? = null
        private fun calcNext() {
            if (iterator.hasNext()) {
                //iterator.next() 調(diào)用的就是上一個 GeneratorSequence 的 next 方法,而返回值就是它的 it+1
                val item = iterator.next()
                //判斷條件,也就是 it <= 100 -> item <= 100
                if (predicate(item)) {
                    nextState = 1
                    nextItem = item
                    return
                }
            }
            nextState = 0
        }
        override fun next(): T {
            if (nextState == -1)
                calcNext() // will change nextState
            if (nextState == 0)
                throw NoSuchElementException()
            @Suppress("UNCHECKED_CAST")
            val result = nextItem as T
            // Clean next to avoid keeping reference on yielded instance
            nextItem = null
            nextState = -1
            return result
        }
        override fun hasNext(): Boolean {
            if (nextState == -1)
                calcNext() // will change nextState
            return nextState == 1
        }
    }
}

TakeWhileSequencenext 方法中,會優(yōu)先調(diào)用內(nèi)部方法 calcNext,而這個方法內(nèi)部又是調(diào)用 GeneratorSequencenext方法,這樣就 拿到了當(dāng)前值 it+1(上一個是0+1,下一個就是1+1),拿到值后再判斷 it <= 100 -> item <= 100。

public fun Sequence<Int>.sum(): Int {
    var sum: Int = 0
    for (element in this) {
        sum += element
    }
    return sum
}

sum 方法是序列的末端操作,也就是獲取結(jié)果。for (element in this) ,調(diào)用上一個 Sequence 中的迭代器 Iterator 進(jìn)行元素迭代,以此類推,直到調(diào)用 源 Sequence 中的迭代器 Iterator 進(jìn)行元素迭代。

總結(jié)

Kotlin 標(biāo)準(zhǔn)庫提供的集合操作函數(shù):filter,map, flatmap 等,在操作的時候會創(chuàng)建存儲中間結(jié)果的臨時列表,當(dāng)集合元素較多時,這種鏈?zhǔn)讲僮骶蜁兊玫托?。為了解決這種問題,Kotlin 提供了惰性集合操作 Sequence 接口,只有在 末端操作被調(diào)用的時候,也就是獲取結(jié)果的時候,序列中的元素才會被逐個執(zhí)行,處理完第一個元素后,才會處理第二個元素,這樣中間操作是被延期執(zhí)行的。而且因為是順序地去執(zhí)行每一個元素,所以可以先做 filter 變換,再做 map 變換,這樣有助于減少變換的總次數(shù)。

以上就是Kotlin惰性集合操作之Sequence序列使用示例的詳細(xì)內(nèi)容,更多關(guān)于Kotlin惰性集合Sequence序列的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • flutter中的資源和圖片加載示例詳解

    flutter中的資源和圖片加載示例詳解

    這篇文章主要為大家介紹了flutter中的資源和圖片加載示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 淺談Android中多線程切換的幾種方法

    淺談Android中多線程切換的幾種方法

    本篇文章主要介紹了淺談Android中多線程切換的幾種方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • Android序列化實現(xiàn)接口Serializable與Parcelable詳解

    Android序列化實現(xiàn)接口Serializable與Parcelable詳解

    我們使用 Intent 傳遞數(shù)據(jù)的時候,putExtra() 所支持的數(shù)據(jù)類型事有限的,當(dāng)需要傳遞自定義對象的時候就需要序列化。Serializable更簡單但是會把整個對象進(jìn)行序列化因此效率比Parcelable低一些
    2022-12-12
  • android ContentResolver獲取手機(jī)電話號碼和短信內(nèi)容

    android ContentResolver獲取手機(jī)電話號碼和短信內(nèi)容

    這篇文章主要為大家詳細(xì)介紹了android ContentResolver獲取手機(jī)電話號碼、短信內(nèi)容,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • Android 解決sqlite無法創(chuàng)建新表的問題

    Android 解決sqlite無法創(chuàng)建新表的問題

    這篇文章主要介紹了Android 解決sqlite無法創(chuàng)建新表的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • android圖像繪制(七)ClipRect局部繪圖/切割原圖繪制總結(jié)

    android圖像繪制(七)ClipRect局部繪圖/切割原圖繪制總結(jié)

    這幾天開始學(xué)游戲地圖制作,今天小小的總結(jié)一下Canvas的clipRect()接口的使用,接下來介紹ClipRect局部繪圖/切割原圖繪制感興趣的朋友可以了解下
    2013-01-01
  • 解決Android studio模擬器啟動失敗的問題

    解決Android studio模擬器啟動失敗的問題

    這篇文章主要介紹了Android studio模擬器啟動失敗的問題及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Android 實現(xiàn)微信登錄詳解

    Android 實現(xiàn)微信登錄詳解

    本文主要介紹Android 微信登錄分享朋友圈,這里給大家詳細(xì)介紹了Android微信登錄的詳細(xì)流程,有需要的小伙伴可以參考下
    2016-07-07
  • Android 超詳細(xì)講解fitsSystemWindows屬性的使用

    Android 超詳細(xì)講解fitsSystemWindows屬性的使用

    fitsSystemWindows屬性可以讓view根據(jù)系統(tǒng)窗口來調(diào)整自己的布局;簡單點說就是我們在設(shè)置應(yīng)用布局時是否考慮系統(tǒng)窗口布局,這里系統(tǒng)窗口包括系統(tǒng)狀態(tài)欄、導(dǎo)航欄、輸入法等,包括一些手機(jī)系統(tǒng)帶有的底部虛擬按鍵
    2022-03-03
  • Fultter NestedScrollView實現(xiàn)吸頂效果以及遇到問題解析

    Fultter NestedScrollView實現(xiàn)吸頂效果以及遇到問題解析

    這篇文章主要為大家介紹了Fultter NestedScrollView實現(xiàn)吸頂效果以及遇到問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09

最新評論