Kotlin惰性集合操作之Sequence序列使用示例
集合操作函數(shù) 和 序列
在了解 Kotlin 惰性集合之前,先看一下 Koltin 標(biāo)注庫中的一些集合操作函數(shù)。
定義一個(gè)數(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 歲人的個(gè)數(shù)
people.count { it.age < 30 }
flatmap 操作:
val books = listOf<Book>(
Book("Java 語言程序設(shè)計(jì)", arrayListOf("xiaowang", "xiaozhang")),
Book("Kotlin 語言程序設(shè)計(jì)", arrayListOf("xiaoli", "xiaomao")),
)
// 所有書的名字集合列表
books.flatMap { it.authors }.toList()
在上面這些函數(shù),每做一步操作,都會(huì)創(chuàng)建中間集合,也就是每一步的中間結(jié)果都被臨時(shí)存儲(chǔ)在一個(gè)臨時(shí)集合中。
filter 函數(shù)源碼:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
//創(chuàng)建一個(gè)新的集合列表
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)建一個(gè)新的集合列表
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個(gè)、100個(gè),那么 函數(shù)鏈?zhǔn)秸{(diào)用 如:fliter{}.map{} 就會(huì)變得低效,且浪費(fèi)內(nèi)存。
Kotlin 為解決上面這種問題,提供了惰性集合操作 Sequence 接口。這個(gè)接口表示一個(gè)可以逐個(gè)列舉的元素列表。Sequence 只提供了一個(gè) 方法, 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)讲僮?,而不需要?jiǎng)?chuàng)建額外的集合來保存過程中產(chǎn)生的中間結(jié)果。關(guān)于這個(gè)惰性是怎么來的,后面再詳細(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è)計(jì)", arrayListOf("xiaowang", "xiaozhang")),
Book("Kotlin 語言程序設(shè)計(jì)", arrayListOf("xiaoli", "xiaomao")),
)
books.asSequence().flatMap { it.authors }.toList()
序列中間和末端操作
序列操作分為兩類:中間的和末端的。一次中間操作返回的是另一個(gè)序列,這個(gè)新序列知道如何變換原始序列中的元素。而一次末端返回的是一個(gè)結(jié)果,這個(gè)結(jié)果可能是集合、元素、數(shù)字,或者其他從初始集合的變換序列中獲取的任意對象。

中間操作始終是惰性的。
下面從例子來理解這個(gè)惰性:
listOf(1, 2, 3, 4).asSequence().map {
println("map${it}")
it * it
}.filter {
println("filter${it}")
it % 2 == 0
}
上面這段代碼在控制臺(tái)不會(huì)輸出任何內(nèi)容(因?yàn)闆]有末端操作)。
listOf(1, 2, 3, 4).asSequence().map {
println("map${it}")
it * it
}.filter {
println("filter${it}")
it % 2 == 0
}.toList()
控制臺(tái)輸出:
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()的時(shí)候,map 和 filter 變換才被執(zhí)行,而且元素是被逐個(gè)執(zhí)行的。并不是所有元素經(jīng)在 map 操作執(zhí)行完成后,再執(zhí)行 filter 操作。
為什么元素是逐個(gè)被執(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),其實(shí)就是調(diào)用 Sequence 中的迭代器 Iterator 進(jìn)行元素迭代。其中這個(gè) this 來自于 filter,也就是使用 filter 的 Iterator 進(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 中又會(huì)使用上一個(gè) Sequence 的 sequence.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)
}
}
也是使用上一個(gè) Sequence 的 sequence.iterator() 進(jìn)行元素迭代。所以以此類推,最終會(huì)使用轉(zhuǎn)換為 asSequence() 的源 iterator()。
下面自定義一個(gè) Sequence 來驗(yàn)證上面的猜想:
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()
}
}
}
控制臺(tái)輸出:
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é)果的時(shí)候才會(huì)被應(yīng)用,也就是末端操作被調(diào)用的時(shí)候,才會(huì)依次處理每個(gè)元素,這也是 被稱為惰性集合操作的原因。
經(jīng)過一系列的 序列操作,每個(gè)元素逐個(gè)被處理,那么優(yōu)先處理 filter 序列,其實(shí)可以減少變換的總次數(shù)。因?yàn)槊總€(gè)序列都是使用上一個(gè)序列的 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)
控制臺(tái)輸出:
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 的第一個(gè)參數(shù) 0
//getNextValue 獲取到的就是 generateSequence 的第二個(gè)參數(shù) it+1,這個(gè)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
}
}
}
上面代碼其實(shí)就是創(chuàng)建一個(gè) Sequence 接口實(shí)現(xiàn)類,并實(shí)現(xiàn)它的 iterator 接口方法,返回一個(gè) 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)用的就是上一個(gè) 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
}
}
}
在 TakeWhileSequence 的 next 方法中,會(huì)優(yōu)先調(diào)用內(nèi)部方法 calcNext,而這個(gè)方法內(nèi)部又是調(diào)用 GeneratorSequence的 next方法,這樣就 拿到了當(dāng)前值 it+1(上一個(gè)是0+1,下一個(gè)就是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)用上一個(gè) Sequence 中的迭代器 Iterator 進(jìn)行元素迭代,以此類推,直到調(diào)用 源 Sequence 中的迭代器 Iterator 進(jìn)行元素迭代。
總結(jié)
Kotlin 標(biāo)準(zhǔn)庫提供的集合操作函數(shù):filter,map, flatmap 等,在操作的時(shí)候會(huì)創(chuàng)建存儲(chǔ)中間結(jié)果的臨時(shí)列表,當(dāng)集合元素較多時(shí),這種鏈?zhǔn)讲僮骶蜁?huì)變得低效。為了解決這種問題,Kotlin 提供了惰性集合操作 Sequence 接口,只有在 末端操作被調(diào)用的時(shí)候,也就是獲取結(jié)果的時(shí)候,序列中的元素才會(huì)被逐個(gè)執(zhí)行,處理完第一個(gè)元素后,才會(huì)處理第二個(gè)元素,這樣中間操作是被延期執(zhí)行的。而且因?yàn)槭琼樞虻厝?zhí)行每一個(gè)元素,所以可以先做 filter 變換,再做 map 變換,這樣有助于減少變換的總次數(shù)。
以上就是Kotlin惰性集合操作之Sequence序列使用示例的詳細(xì)內(nèi)容,更多關(guān)于Kotlin惰性集合Sequence序列的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android?RxJava與Retrofit結(jié)合使用詳解
RxJava和Retrofit的結(jié)合使用估計(jì)已經(jīng)相當(dāng)普遍了,自己工作中也是一直都在使用。在使用的過程中我們都會(huì)對其進(jìn)行封裝使用,GitHub上也有很多封裝好的項(xiàng)目可以直接拿來使用,其實(shí)對于開源框架的二次封裝有時(shí)候針對不同的業(yè)務(wù)邏輯封裝的過程中也多多少少有些不同2023-03-03
WheelView實(shí)現(xiàn)上下滑動(dòng)選擇器
這篇文章主要為大家詳細(xì)介紹了WheelView實(shí)現(xiàn)上下滑動(dòng)選擇器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Flutter啟動(dòng)頁(閃屏頁)的具體實(shí)現(xiàn)及原理詳析
這篇文章主要給大家介紹了關(guān)于Flutter啟動(dòng)頁(閃屏頁)的具體實(shí)現(xiàn)及原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
使用Android studio編寫一個(gè)小的jni程序
JNI是Java Native Interface的縮寫,它提供了若干的API實(shí)現(xiàn)了Java和其他語言的通信(主要是C&C++)。這篇文章給大家介紹了基于Android studio寫一個(gè)小的jni程序的方法,一起看看吧2018-03-03
點(diǎn)擊微信內(nèi)網(wǎng)頁a標(biāo)簽直接跳轉(zhuǎn)打開淘寶APP的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于如何實(shí)現(xiàn)點(diǎn)擊微信內(nèi)網(wǎng)頁a標(biāo)簽直接跳轉(zhuǎn)打開淘寶APP的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-11-11
Android編程實(shí)現(xiàn)屏幕自適應(yīng)方向尺寸與分辨率的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)屏幕自適應(yīng)方向尺寸與分辨率的方法,涉及Android屏幕分辨率、布局、橫豎屏切換等相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12
Android中Glide加載到RelativeLayout背景圖方法示例
Glide框架大家應(yīng)該都很熟悉,我們可以使用Glide加載網(wǎng)絡(luò)圖片、加載gif圖片,使用簡單。下面這篇文章主要給大家介紹了關(guān)于Android中Glide加載到RelativeLayout背景圖的相關(guān)資料,需要的朋友可以參考下。2017-12-12
Android編程實(shí)現(xiàn)實(shí)時(shí)監(jiān)聽EditText文本輸入的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)實(shí)時(shí)監(jiān)聽EditText文本輸入的方法,結(jié)合實(shí)例形式分析了EditText控件及事件響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下2017-06-06
Android ViewPager實(shí)現(xiàn)滑動(dòng)指示條功能
這篇文章主要介紹了Android-ViewPager實(shí)現(xiàn)滑動(dòng)指示條功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

