對(duì)于遍歷集合元素,Kotlin 標(biāo)準(zhǔn)庫(kù)支持 迭代器 的常用機(jī)制?對(duì)象可按順序提供對(duì)元素的訪問(wèn)權(quán)限,而 不會(huì)暴露集合的底層結(jié)構(gòu)。當(dāng)需要逐個(gè)處理集合的所有元素(例如打印值或?qū)ζ溥M(jìn)行類似更新)時(shí),迭代 器非常有用。
創(chuàng)新互聯(lián)建站主營(yíng)湖州網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都App定制開(kāi)發(fā),湖州h5微信小程序開(kāi)發(fā)搭建,湖州網(wǎng)站營(yíng)銷推廣歡迎湖州等地區(qū)企業(yè)咨詢Iterable
val numbers = listOf("one", "two", "three", "four")
val numbersIterator= numbers.iterator()
while (numbersIterator.hasNext()) {
println(numbersIterator.next())
}
遍歷 Iterable 集合的另一種方法是眾所周知的 for 循環(huán)。在集合中使用 for 循環(huán)時(shí),將隱式獲取 迭代器。因此,以下代碼與上面的示例等效
val numbers = listOf("one", "two", "three", "four")
for (item in numbers) {
println(item)
}
最后,有一個(gè)好用的 forEach() 函數(shù),可自動(dòng)迭代集合并為每個(gè)元素執(zhí)行給定的代碼。因此,等效的示 例如下所示:
val numbers = listOf("one", "two", "three", "four")
numbers.forEach {
println(it)
}
1.1 List迭代器
對(duì)于列表,有一個(gè)特殊的迭代器實(shí)現(xiàn):ListIterator 它支持列表雙向迭代:正向與反向。反向迭代由 hasPrevious() 和 previous() 函數(shù)實(shí)現(xiàn)。此外,ListIterator 通過(guò) nextIndex() 與 previousIndex() 函數(shù)提供有關(guān)元素索引的信息。
val numbers = listOf("one", "two", "three", "four")
val listIterator= numbers.listIterator()
while (listIterator.hasNext()){
listIterator.next()
}
while (listIterator.hasPrevious()) {
print("Index: ${listIterator.previousIndex()}")
println (", value: ${listIterator.previous()}")
}
具有雙向迭代的能力意味著 ListIterator 在到達(dá)最后一個(gè)元素后仍可以使用
1.2 可變迭代器
為了迭代可變集合,于是有了 MutableIterator 來(lái)擴(kuò)展 Iterator 使其具有元素刪除函數(shù) remove() 。因此,可以在迭代時(shí)從集合中刪除元素
val numbers = mutableListOf("one", "two", "three", "four")
val mutableIterator= numbers.iterator()
mutableIterator.next()
mutableIterator.remove()
println("After removal: $numbers")
除了刪除元素,MutableListIterator 還可以在迭代列表時(shí)插入和替換元素。
val numbers = mutableListOf("one", "four", "four")
val mutableListIterator= numbers.listIterator()
mutableListIterator.next()
mutableListIterator.add("two")
mutableListIterator.next()
mutableListIterator.set("three")
println(numbers)
2.區(qū)間與數(shù)列
Kotlin 可通過(guò)調(diào)用 kotlin.ranges 包中的 rangeTo() 函數(shù)及其操作符形式的 .. 輕松地創(chuàng)建兩 個(gè)值的區(qū)間。通常,rangeTo() 會(huì)輔以 in 或 !in 函數(shù)。
if(i in 1..4){ //等同于1<=i&&i<=4 print(i)
}
整數(shù)類型區(qū)間(IntRange、LongRange、CharRange)還有一個(gè)拓展特性:可以對(duì)其進(jìn)行迭代。這些區(qū)間也是相應(yīng)整數(shù)類型的等差數(shù)列。這種區(qū)間通常用于 for 循環(huán)中的迭代。
for (i in 1..4) print(i)
要反向迭代數(shù)字,請(qǐng)使用 downTo 函數(shù)而不是 .. 。
for (i in 4 downTo 1) print(i)
也可以通過(guò)任意步?(不一定為 1 )迭代數(shù)字。這是通過(guò) step 函數(shù)完成的。
for (i in 1..8 step 2) print(i) //輸出 1357
println()
for (i in 8 downTo 1 step 2) print(i)//輸出 8642
要迭代不包含其結(jié)束元素的數(shù)字區(qū)間,請(qǐng)使用 until 函數(shù):
for (i in 1 until 10) { // i in [1, 10), 10被排除 print(i)
}
2.1 區(qū)間
區(qū)間從數(shù)學(xué)意義上定義了一個(gè)封閉的間隔:它由兩個(gè)端點(diǎn)值定義,這兩個(gè)端點(diǎn)值都包含在該區(qū)間內(nèi)。區(qū)間是為可比較類型定義的:具有順序,可以定義任意實(shí)例是否在兩個(gè)給定實(shí)例之間的區(qū)間內(nèi)。區(qū)間的主要操作是 contains,通常以 in 與 !in 操作符的形式使用。
要為類創(chuàng)建一個(gè)區(qū)間,請(qǐng)?jiān)趨^(qū)間起始值上調(diào)用 rangeTo() 函數(shù),并提供結(jié)束值作為參數(shù)。 rangeTo() 通常以操作符 .. 形式調(diào)用。
val versionRange = Version(1, 11)..Version(1, 30)
println(Version(0, 9) in versionRange)
println(Version(1, 20) in versionRange)
2.2 數(shù)列
如上個(gè)示例所示,整數(shù)類型的區(qū)間(例如 Int 、Long 與 Char )可視為等差數(shù)列。在 Kotlin 中,這些數(shù) 列由特殊類型定義:IntProgression、LongProgression 與 CharProgression。
數(shù)列具有三個(gè)基本屬性:first 元素、last 元素和一個(gè)非零的 step 。首個(gè)元素為 first ,后續(xù)元素是前一個(gè)元素加上一個(gè) step。以確定的步?在數(shù)列上進(jìn)行迭代等效于Java/JavaScript中基于索 引的 for 循環(huán)
for (int i = first; i <= last; i += step) {
// ......}
通過(guò)迭代數(shù)列隱式創(chuàng)建區(qū)間時(shí),此數(shù)列的 first 與 last 元素是區(qū)間的端點(diǎn),step 為 1
for (i in 1..10) print(i)
要指定數(shù)列步?,請(qǐng)?jiān)趨^(qū)間上使用 step 函數(shù)
for (i in 1..8 step 2) print(i)
數(shù)列的 last 元素是這樣計(jì)算的:
— 對(duì)于正步?:不大于結(jié)束值且滿足 (last - first) % step == 0 的大值。
— 對(duì)于負(fù)步?:不小于結(jié)束值且滿足 (last- first) % step == 0 的最小值。
因此,last 元素并非總與指定的結(jié)束值相同
for (i in 1..9 step 3) print(i) // 最后一個(gè)元素是 7
要?jiǎng)?chuàng)建反向迭代的數(shù)列,請(qǐng)?jiān)诙x其區(qū)間時(shí)使用 downTo 而不是 .. 。
for (i in 4 downTo 1) print(i)
數(shù)列實(shí)現(xiàn) Iterable
println((1..10).filter { it % 2 == 0 })
3.序列
除了集合之外,Kotlin 標(biāo)準(zhǔn)庫(kù)還包含另一種容器類型?序列(Sequence
當(dāng) Iterable 的處理包含多個(gè)步驟時(shí),它們會(huì)優(yōu)先執(zhí)行:每個(gè)處理步驟完成并返回其結(jié)果?中間集合。在此集合上執(zhí)行以下步驟。反過(guò)來(lái),序列的多步處理在可能的情況下會(huì)延遲執(zhí)行:僅當(dāng)請(qǐng)求整個(gè)處理鏈的結(jié)果時(shí)才進(jìn)行實(shí)際計(jì)算。
操作執(zhí)行的順序也不同:Sequence 對(duì)每個(gè)元素逐個(gè)執(zhí)行所有處理步驟。反過(guò)來(lái),Iterable 完成整 個(gè)集合的每個(gè)步驟,然后進(jìn)行下一步。
因此,這些序列可避免生成中間步驟的結(jié)果,從而提高了整個(gè)集合處理鏈的性能。但是,序列的延遲性質(zhì) 增加了一些開(kāi)銷,這些開(kāi)銷在處理較小的集合或進(jìn)行更簡(jiǎn)單的計(jì)算時(shí)可能很重要。因此,應(yīng)該同時(shí)考慮 使用 Sequence 與 Iterable,并確定在哪種情況更適合
4.構(gòu)造
4.1 由元素,要?jiǎng)?chuàng)建一個(gè)序列,請(qǐng)調(diào)用 sequenceOf() 函數(shù),列出元素作為其參數(shù)
val numbersSequence = sequenceOf("four", "three", "two", "one")
4.2 由 Iterable,如果已經(jīng)有一個(gè) Iterable 對(duì)象(例如 List 或 Set ),則可以通過(guò)調(diào)用 asSequence() 從而創(chuàng)建一個(gè)序列。
val numbers = listOf("one", "two", "three", "four")
val numbersSequence= numbers.asSequence()
4.3 由函數(shù),創(chuàng)建序列的另一種方法是通過(guò)使用計(jì)算其元素的函數(shù)來(lái)構(gòu)建序列。要基于函數(shù)構(gòu)建序列,請(qǐng)以該函數(shù)作 為參數(shù)調(diào)用 generateSequence()。(可選)可以將第一個(gè)元素指定為顯式值或函數(shù)調(diào)用的結(jié)果。當(dāng)
提供的函數(shù)返回 null 時(shí),序列生成停止。因此,以下示例中的序列是無(wú)限的
val oddNumbers = generateSequence(1) { it + 2 } // `it` 是上一個(gè)元素println(oddNumbers.take(5).toList())
//println(oddNumbers.count())// 錯(cuò)誤:此序列是無(wú)限的。
要使用 generateSequence() 創(chuàng)建有限序列,請(qǐng)?zhí)峁┮粋€(gè)函數(shù),該函數(shù)在需要的最后一個(gè)元素之后 返回 null
val oddNumbersLessThan10 = generateSequence(1) { if (it < 10) it + 2 else null }
println(oddNumbersLessThan10.count())
4.4 由組塊,最后有一個(gè)函數(shù)可以逐個(gè)或按任意大小的組塊生成序列元素sequence( )函數(shù).此函數(shù)采用一個(gè)lambda 表達(dá)式,其中包含 yield() 與 yieldAll() 函數(shù)的調(diào)用。它們將一個(gè)元素返回給序列使用 者,并暫停 sequence() 的執(zhí)行,直到使用者請(qǐng)求下一個(gè)元素。yield() 使用單個(gè)元素作為參 數(shù);yieldAll() 中可以采用 Iterable 對(duì)象、Iterable 或其他 Sequence 。yieldAll() 的Sequence 參數(shù)可以是無(wú)限的。當(dāng)然,這樣的調(diào)用必須是最后一個(gè):之后的所有調(diào)用都永遠(yuǎn)不會(huì)執(zhí)行
val oddNumbers = sequence {
yield(1)
yieldAll(listOf(3, 5))
yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList())
5.序列操作
關(guān)于序列操作,根據(jù)其狀態(tài)要求可以分為以下幾類:
— 無(wú)狀態(tài)操作不需要狀態(tài),并且可以獨(dú)立處理每個(gè)元素,例如map()或filter()。無(wú)狀態(tài)操作還可能需要少量常數(shù)個(gè)狀態(tài)來(lái)處理元素,例如 take() 與 drop()。
— 有狀態(tài)操作需要大量狀態(tài),通常與序列中元素的數(shù)量成比例
如果序列操作返回延遲生成的另一個(gè)序列,則稱為 中間序列。否則,該操作為 末端 操作。末端操作的示 例為 toList() 或 sum()。只能通過(guò)末端操作才能檢索序列元素。
序列可以多次迭代;但是,某些序列實(shí)現(xiàn)可能會(huì)約束自己僅迭代一次。其文檔中特別提到了這一點(diǎn)。
6.序列處理示例
我們通過(guò)一個(gè)示例來(lái)看 Iterable 與 Sequence 之間的區(qū)別
6.1 Iterable,假定有一個(gè)單詞列表。下面的代碼過(guò)濾?于三個(gè)字符的單詞,并打印前四個(gè)單詞的?度
val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList= words.filter { println("filter: $it"); it.length > 3 }
.map { println("length: ${it.length}"); it.length }.take(4)
println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)
運(yùn)行此代碼時(shí),會(huì)看到 filter() 與 map() 函數(shù)的執(zhí)行順序與代碼中出現(xiàn)的順序相同。首先,將看到 filter :對(duì)于所有元素,然后是 length :對(duì)于在過(guò)濾之后剩余的元素,然后是最后兩行的輸出。列表處理如下圖
6.2 Sequence,現(xiàn)在用序列寫(xiě)相同的邏輯
val words = "The quick brown fox jumps over the lazy dog".split(" ")
// 將列表轉(zhuǎn)換為序列val wordsSequence = words.asSequence()
val lengthsSequence= wordsSequence.filter { println("filter: $it"); it.length > 3 }
.map { println("length: ${it.length}"); it.length }
.take(4)
println("Lengths of first 4 words longer than 3 chars") // 末端操作:以列表形式獲取結(jié)果。println(lengthsSequence.toList())
此代碼的輸出表明,僅在構(gòu)建結(jié)果列表時(shí)才調(diào)用 filter() 與 map() 函數(shù)。因此,首先看到文本 “Lengths of..” 的行,然后開(kāi)始進(jìn)行序列處理。請(qǐng)注意,對(duì)于過(guò)濾后剩余的元素,映射在過(guò)濾下一個(gè)元素之前執(zhí)行。當(dāng)結(jié)果大小達(dá)到 4 時(shí),處理將停止,因?yàn)樗?take(4) 可以返回的大大小
序列處理如下圖:在此示例中,序列處理需要 18 個(gè)步驟,而不是 23 個(gè)步驟來(lái)執(zhí)行列表操作