kotlin集合——>迭代器、區間與數列


1.迭代器

  對於遍歷集合元素,Kotlin 標准庫支持 迭代器 的常用機制⸺對象可按順序提供對元素的訪問權限,而 不會暴露集合的底層結構。當需要逐個處理集合的所有元素(例如打印值或對其進行類似更新)時,迭代 器非常有用。

  Iterable<T> 接口的繼承者(包括 Set 與 List )可以通過調用 iterator() 函數獲得迭代器。一 旦獲得迭代器它就指向集合的第一個元素;調用 next() 函數將返回此元素,並將迭代器指向下一個元素(如果下一個元素存在)。一旦迭代器通過了最后一個元素,它就不能再用於檢索元素;也無法重新指 向到以前的任何位置。要再次遍歷集合,請創建一個新的迭代器。

val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.iterator()
while (numbersIterator.hasNext()) {
    println(numbersIterator.next())
}

遍歷 Iterable 集合的另一種方法是眾所周知的 for 循環。在集合中使用 for 循環時,將隱式獲取 迭代器。因此,以下代碼與上面的示例等效

val numbers = listOf("one", "two", "three", "four")
for (item in numbers) {
    println(item)
}

最后,有一個好用的 forEach() 函數,可自動迭代集合並為每個元素執行給定的代碼。因此,等效的示 例如下所示:

val numbers = listOf("one", "two", "three", "four") 
numbers.forEach {
    println(it)
}

  1.1 List迭代器

    對於列表,有一個特殊的迭代器實現:ListIterator 它支持列表雙向迭代:正向與反向。反向迭代由 hasPrevious() 和 previous() 函數實現。此外,ListIterator 通過 nextIndex() 與 previousIndex() 函數提供有關元素索引的信息。

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 在到達最后一個元素后仍可以使用

  1.2 可變迭代器

    為了迭代可變集合,於是有了 MutableIterator 來擴展 Iterator 使其具有元素刪除函數 remove() 。因此,可以在迭代時從集合中刪除元素

val numbers = mutableListOf("one", "two", "three", "four") 
val mutableIterator = numbers.iterator()
mutableIterator.next() 
mutableIterator.remove() 
println("After removal: $numbers")

    除了刪除元素,MutableListIterator 還可以在迭代列表時插入和替換元素。

val numbers = mutableListOf("one", "four", "four") 
val mutableListIterator = numbers.listIterator()

mutableListIterator.next() 
mutableListIterator.add("two") 
mutableListIterator.next() 
mutableListIterator.set("three") 
println(numbers)

2.區間與數列

  Kotlin 可通過調用 kotlin.ranges 包中的 rangeTo() 函數及其操作符形式的 .. 輕松地創建兩 個值的區間。通常,rangeTo() 會輔以 in 或 !in 函數。

if(i in 1..4){ //等同於1<=i&&i<=4
   print(i)
}

  整數類型區間(IntRange、LongRange、CharRange)還有一個拓展特性:可以對其進行迭代。這些區間也是相應整數類型的等差數列。這種區間通常用於 for 循環中的迭代。

for (i in 1..4) print(i)

  要反向迭代數字,請使用 downTo 函數而不是 .. 。

for (i in 4 downTo 1) print(i)

  也可以通過任意步⻓(不一定為 1 )迭代數字。這是通過 step 函數完成的。

for (i in 1..8 step 2) print(i) //輸出 1357
println()
for (i in 8 downTo 1 step 2) print(i)//輸出 8642

  要迭代不包含其結束元素的數字區間,請使用 until 函數:

for (i in 1 until 10) { // i in [1, 10), 10被排除
  print(i)
}

  2.1 區間

    區間從數學意義上定義了一個封閉的間隔:它由兩個端點值定義,這兩個端點值都包含在該區間內。區間是為可比較類型定義的:具有順序,可以定義任意實例是否在兩個給定實例之間的區間內。區間的主要操作是 contains,通常以 in 與 !in 操作符的形式使用。

    要為類創建一個區間,請在區間起始值上調用 rangeTo() 函數,並提供結束值作為參數。 rangeTo() 通常以操作符 .. 形式調用。

val versionRange = Version(1, 11)..Version(1, 30)
println(Version(0, 9) in versionRange)
println(Version(1, 20) in versionRange)

  2.2 數列

    如上個示例所示,整數類型的區間(例如 Int 、Long 與 Char )可視為等差數列。在 Kotlin 中,這些數 列由特殊類型定義:IntProgression、LongProgression 與 CharProgression。

    數列具有三個基本屬性:first 元素、last 元素和一個非零的 step 。首個元素為 first ,后續元素是前一個元素加上一個 step。以確定的步⻓在數列上進行迭代等效於Java/JavaScript中基於索 引的 for 循環

for (int i = first; i <= last; i += step) {
     // ......
}

    通過迭代數列隱式創建區間時,此數列的 first 與 last 元素是區間的端點,step 為 1

for (i in 1..10) print(i)

    要指定數列步⻓,請在區間上使用 step 函數

for (i in 1..8 step 2) print(i)

    數列的 last 元素是這樣計算的:

— 對於正步⻓:不大於結束值且滿足 (last - first) % step == 0 的最大值。
— 對於負步⻓:不小於結束值且滿足 (last - first) % step == 0 的最小值。

    因此,last 元素並非總與指定的結束值相同

for (i in 1..9 step 3) print(i) // 最后一個元素是 7

    要創建反向迭代的數列,請在定義其區間時使用 downTo 而不是 .. 。

for (i in 4 downTo 1) print(i)

    數列實現 Iterable<N>,其中 N 分別是 Int、Long 或 Char,因此可以在各種集合函數(如map、filter 與其他)中使用它們

println((1..10).filter { it % 2 == 0 })

3.序列

  除了集合之外,Kotlin 標准庫還包含另一種容器類型⸺序列(Sequence<T>)。序列提供與 Iterable 相同的函數,但實現另一種方法來進行多步驟集合處理。

  當 Iterable 的處理包含多個步驟時,它們會優先執行:每個處理步驟完成並返回其結果⸺中間集合。在此集合上執行以下步驟。反過來,序列的多步處理在可能的情況下會延遲執行:僅當請求整個處理鏈的結果時才進行實際計算。

  操作執行的順序也不同:Sequence 對每個元素逐個執行所有處理步驟。反過來,Iterable 完成整 個集合的每個步驟,然后進行下一步。

  因此,這些序列可避免生成中間步驟的結果,從而提高了整個集合處理鏈的性能。但是,序列的延遲性質 增加了一些開銷,這些開銷在處理較小的集合或進行更簡單的計算時可能很重要。因此,應該同時考慮 使用 Sequence 與 Iterable,並確定在哪種情況更適合

4.構造

  4.1 由元素,要創建一個序列,請調用 sequenceOf() 函數,列出元素作為其參數

val numbersSequence = sequenceOf("four", "three", "two", "one")

  4.2 由 Iterable,如果已經有一個 Iterable 對象(例如 List 或 Set ),則可以通過調用 asSequence() 從而創建一個序列。

val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()

  4.3 由函數,創建序列的另一種方法是通過使用計算其元素的函數來構建序列。要基於函數構建序列,請以該函數作 為參數調用 generateSequence()。(可選)可以將第一個元素指定為顯式值或函數調用的結果。當

提供的函數返回 null 時,序列生成停止。因此,以下示例中的序列是無限的

val oddNumbers = generateSequence(1) { it + 2 } // `it` 是上一個元素 
println(oddNumbers.take(5).toList())
//println(oddNumbers.count()) // 錯誤:此序列是無限的。

    要使用 generateSequence() 創建有限序列,請提供一個函數,該函數在需要的最后一個元素之后 返回 null

val oddNumbersLessThan10 = generateSequence(1) { if (it < 10) it + 2 else null } 
println(oddNumbersLessThan10.count())

  4.4 由組塊,最后有一個函數可以逐個或按任意大小的組塊生成序列元素sequence( )函數.此函數采用一個lambda 表達式,其中包含 yield() 與 yieldAll() 函數的調用。它們將一個元素返回給序列使用 者,並暫停 sequence() 的執行,直到使用者請求下一個元素。yield() 使用單個元素作為參 數;yieldAll() 中可以采用 Iterable 對象、Iterable 或其他 Sequence 。yieldAll() 的Sequence 參數可以是無限的。當然,這樣的調用必須是最后一個:之后的所有調用都永遠不會執行

val oddNumbers = sequence {
    yield(1)
    yieldAll(listOf(3, 5))
    yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList())

5.序列操作

  關於序列操作,根據其狀態要求可以分為以下幾類:

— 無狀態操作不需要狀態,並且可以獨立處理每個元素,例如map()或filter()。無狀態操作還可能需要少量常數個狀態來處理元素,例如 take() 與 drop()。
— 有狀態操作需要大量狀態,通常與序列中元素的數量成比例

  如果序列操作返回延遲生成的另一個序列,則稱為 中間序列。否則,該操作為 末端 操作。末端操作的示 例為 toList() 或 sum()。只能通過末端操作才能檢索序列元素。

  序列可以多次迭代;但是,某些序列實現可能會約束自己僅迭代一次。其文檔中特別提到了這一點。

6.序列處理示例

  我們通過一個示例來看 Iterable 與 Sequence 之間的區別

  6.1 Iterable,假定有一個單詞列表。下面的代碼過濾⻓於三個字符的單詞,並打印前四個單詞的⻓度

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)

  運行此代碼時,會看到 filter() 與 map() 函數的執行順序與代碼中出現的順序相同。首先,將看到 filter :對於所有元素,然后是 length :對於在過濾之后剩余的元素,然后是最后兩行的輸出。列表處理如下圖

  6.2 Sequence,現在用序列寫相同的邏輯

val words = "The quick brown fox jumps over the lazy dog".split(" ") 
// 將列表轉換為序列
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") // 末端操作:以列表形式獲取結果。
println(lengthsSequence.toList())

  此代碼的輸出表明,僅在構建結果列表時才調用 filter() 與 map() 函數。因此,首先看到文本 “Lengths of..” 的行,然后開始進行序列處理。請注意,對於過濾后剩余的元素,映射在過濾下一個元素之前執行。當結果大小達到 4 時,處理將停止,因為它是 take(4) 可以返回的最大大小

  序列處理如下圖:在此示例中,序列處理需要 18 個步驟,而不是 23 個步驟來執行列表操作

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM