码迷,mamicode.com
首页 > 其他好文 > 详细

kotlin集合——>迭代器、区间与数列

时间:2020-07-02 16:42:42      阅读:37      评论:0      收藏:0      [点我收藏+]

标签:完成   index   ogre   iter   previous   实例   first   有关   间隔   

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 个步骤来执行列表操作

  

kotlin集合——>迭代器、区间与数列

标签:完成   index   ogre   iter   previous   实例   first   有关   间隔   

原文地址:https://www.cnblogs.com/developer-wang/p/13225247.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!