zoukankan      html  css  js  c++  java
  • 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 个步骤来执行列表操作

      

  • 相关阅读:
    Android杂谈ubuntu系统下adb连接小米2
    Android UI设计ListView的页脚(footer)的使用
    Android杂谈关于Android的nodpi,xhdpi,hdpi,mdpi,ldpi
    ”该证书已被签发机构吊销“错误解决方案
    Android杂谈RelativeLayout中的baseline是什么?
    ubuntu下git更改默认编辑器
    Android UI设计ListView Item的OnItemLongClickListener同时监听两种事件
    Android UI设计ListView的item选中效果
    Ubuntu下ssh服务器文件操作命令
    ubuntu下emacs的配置(cedit,ecb)
  • 原文地址:https://www.cnblogs.com/developer-wang/p/13225247.html
Copyright © 2011-2022 走看看