Kotlin系列之四-集合

Kotlin系列之四-集合

一.Kotlin中的集合

1.相关集合类型:
    -List:有序
        1).var multableListOf=multableListOf(),可变集合,不是型变的
        2).var listOf=listOf(),只读集合,型变的,在需要父类的地方可以传入子类
    -Set:唯一
    -Map:键值对
2.集合的根
    Collection
3.List在Kotlin的默认实现是ArrayList
4.可操作的List是MutableList,可以进行增,删,改,查
5.Set:
    1.存储的元素不可重,有唯一性
    2.元素可以有且只能有1个null
    3.MutableSet是带有来自MutableCollection的写操作接口的Set
    4.Set的默认实现是:LinkedHashSet-保留原素的插入顺序,所以first(),last()等函数可以拿到期望的元素
        而另外一种HashSet是无序的,但是同样多元素多的时候,它比LinkedHashSet占用更少的内存
6.Map
    1.Map并不是Collection的子类
    2.该键值对,键必须是唯一的,值可以重复
    fun testMap() {
        var map = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
        //取元素
        var key1Value = map["key1"]
        println("key1 对应的元素值为 $key1Value")
        println("all keys ${map.keys}")
        println("all values ${map.values}")
        if ("key2" in map) {
            println("value by key2 is ${map["key2"]}")
        }
        if (1 in map.values||map.containsValue(1)){
            println("1 is in map value")
        }
    }
    3.map的默认实现是LinkedHashMap,有序,而另一种实现HashMap是无序的
7.构造集合
    1.使用Kotlin api也就是标准的库函数
        listOf(),mapOf(),setOf(),mutableListOf(),mutableSetOf()
    2.如果构造时,指定元素,那么编译器会自动推测类型,但是创建空集合时,必须指定元素类型
    3.map的创建时,是接收了由to创建的Pair对象
        注意,to仅在不注重性能的时候推荐使用,若想考虑性能,请使用apply()函数
    4.创建空集合:emptyList(),emptySet(),emptyMap()
    5.List的初始化函数
         val doubled = List(5, { it * 2 })
            println(doubled)//[0, 2, 4, 6, 8]
    6.集合的复制,toListOf(),toMapOf(),toSetOf(),源集合的改变不会影响副本集合,同样,副本集合也独立于源集合进行更改
        有时也可以把集合转化为其他类型:
              val sourceList = mutableListOf(1, 2, 3)
                var toSet = sourceList.toMutableSet()
                toSet.add(999)
                println(toSet) // [1, 2, 3, 999]               
    7.调用其他集合的函数
        1).过滤
            val numbers = listOf(1, 2, 4)
            var filter = numbers.filter { it>3}
            println(filter)//[4]
        2).映射生成转换结果列表:
            var numbers = setOf(1, 2, 3)
            var map = numbers.map { it + 1 }
            println(map + "-" + map.javaClass)//[2, 3, 4, -, class java.util.ArrayList]
            var mapIndexed = numbers.mapIndexed { idx, value ->
                value * idx
            }
            println(mapIndexed + "-" + mapIndexed.javaClass)  // [0, 2, 6, -, class java.util.ArrayList]     
        3).关联生成map
            val numbers = listOf("one", "two", "three", "four")
            var message = numbers.associateWith { it.length }
            println(message)//{one=3, two=3, three=5, four=4}
            println(" " + message.javaClass)// class java.util.LinkedHashMap
8.迭代器
   1.迭代器的常用机制:对象可按顺序提供对元素的访问权限,而不会暴露集合的底层结构
   2.List,Set继承自Iterable接口,可以使用iterator()方法获取迭代器
        val numbers = listOf(1, 2, 3, 4, 5)
        var iterator = numbers.iterator()
        while (iterator.hasNext()) {
            println(iterator.next())
        }
        println("--------")
        for (number in numbers) {
            println(number)
        }
        println("--------")
        numbers.forEach { println(it) }
    3.List具有双向迭代器ListIterator
        val numbers = listOf(1, 2, 3, 4, 5)
        var listIterator = numbers.listIterator()
        while (listIterator.hasNext()) {
            println("next Index is ${listIterator.nextIndex()} and next value is ${listIterator.next()}")
        }
        while (listIterator.hasPrevious()) {
            println("previous index is ${listIterator.previousIndex()} and previous value is ${listIterator.previous()}")
        }
        //next Index is 1 and next value is 2
        //next Index is 2 and next value is 3
        //next Index is 3 and next value is 4
        //next Index is 4 and next value is 5
        //previous index is 4 and previous value is 5
        //previous index is 3 and previous value is 4
        //previous index is 2 and previous value is 3
        //previous index is 1 and previous value is 2
        //previous index is 0 and previous value is 1
    4.可变迭代器:MutableIterator(删除)/MutableListIterator(插入/替换)
        var numbers = mutableListOf("1", "2", "3")
        var iterator = numbers.iterator()
        println(iterator.javaClass)
        iterator.next()//删掉这句话直接报错
        iterator.remove()
        println("numbers after removed :$numbers")              
9.区间与数列
    1.区间
        1).Kotlin通过kotlin.range保重的rangeTo()及其操作数形式的..创建两个值的区间,通常rangeTo会辅以in或!in函数
            var i = 3
            if (i in 1..4) {
                println(true)//true
            }
        2).要想反向迭代数字使用关键字downTo,而不是..
            var i = 3
            if (i in 4 downTo 0) {
                println(true)//true
            }
        3).也可以通过任意步长迭代数字:
            for (i in 1..4 step 2) {
                println(i)//1,3
            }
        4).要想排除区间的某个数,使用until
            for (i in 1 until 4) {
                println(i)//1,2,3
            }
    2.数列
        1.整数类型的区间可以视为等差数列:IntProgression、LongProgression 与 CharProgression。
        2.数列具有三个基本属性:first 元素、last 元素和一个非零的 step 。
            数列的 last 元素是这样计算的:
                — 对于正步⻓:不大于结束值且满足 (last - first) % step == 0 的最大值。
                — 对于负步⻓:不小于结束值且满足 (last - first) % step == 0 的最小值。
                for (i in 1..9 step 4){
                    println(i)//1,5,9
                }
                var message = (1..10).filter { it % 2 == 0 }
                println(message)//[2, 4, 6, 8, 10]
10.序列
    1.除了集合之外,Kotlin 标准库还包含另一种容器类型⸺序列(Sequence)。序列提供与 Iterable 相同的函数,但实现另一种方法来进行多步骤集合处理。
    当 Iterable 的处理包含多个步骤时,它们会优先执行:每个处理步骤完成并返回其结果⸺中间集 合。在此集合上执行以下步骤。反过来,序列的多步处理在可能的情况下会延迟执行:仅当请求整个处理
    链的结果时才进行实际计算。
    操作执行的顺序也不同:Sequence 对每个元素逐个执行所有处理步骤。反过来,Iterable 完成整 个集合的每个步骤,然后进行下一步。
    因此,这些序列可避免生成中间步骤的结果,从而提高了整个集合处理链的性能。但是,序列的延迟性质 增加了一些开销,这些开销在处理较小的集合或进行更简单的计算时可能很重要。因此,应该同时考虑 使用 Sequence 与 Iterable,并确定在哪种情况更适合。

二.集合的操作

Kotlin 标准库提供了用于对集合执行操作的多种函数。这包括简单的操作,例如获取或添加元素,以及 更复杂的操作,包括搜索、排序、过滤、转换等。
扩展与成员函数
集合操作在标准库中以两种方式声明:集合接口的成员函数和扩展函数。 成员函数定义了对于集合类型是必不可少的操作。例如,Collection 包含函数 isEmpty() 来检查其
是 否 为 空 ; L i s t 包 含 用 于 对 元 素 进 行 索 引 访 问 的 g e t ( ) ,等 等 。 创建自己的集合接口实现时,必须实现其成员函数。为了使新实现的创建更加容易,请使用标准库中集
合接口的框架实现:AbstractCollection、AbstractList、AbstractSet、AbstractMap 及其 相应可变抽象类。
其他集合操作被声明为扩展函数。这些是过滤、转换、排序和其他集合处理功能。

公共操作

公共操作可用于只读集合与可变集合。常⻅操作分为以下几类:
— 集合转换
— 集合过滤
— plus 与 minus 操作符
— 分组
— 取集合的一部分
— 取单个元素
— 集合排序
— 集合聚合操作

这些⻚面中描述的操作将返回其结果,而不会影响原始集合。例如,一个过滤操作产生一个新集合,其 中包含与过滤谓词匹配的所有元素。此类操作的结果应存储在变量中,或以其他方式使用,例如,传到其 他函数中。
对于某些集合操作,有一个选项可以指定 目标 对象。目标是一个可变集合,该函数将其结果项附加到该 可变对象中,而不是在新对象中返回它们。对于执行带有目标的操作,有单独的函数,其名称中带有 To
后缀,例如,用 filterTo() 代替 filter() 以及用 associateTo() 代替 associate()。这些函 数将目标集合作为附加参数。

   val numbers = listOf("one", "two", "three", "four")
   numbers.filter { it.length > 3 } // `numbers` 没有任何改变,结果丢失 println("numbers are still $numbers")
   val longerThan3 = numbers.filter { it.length > 3 } // 结果存储在 `longerThan3` 中 println("numbers longer than 3 chars are $longerThan3")
   val numbers = listOf("one", "two", "three", "four")
   val filterResults = mutableListOf() // 目标对象 numbers.filterTo(filterResults) { it.length > 3 } numbers.filterIndexedTo(filterResults) { index, _ -> index == 0 } println(filterResults) // 包含两个操作的结果

为了方便起⻅,这些函数将目标集合返回了,因此您可以在函数调用的相应参数中直接创建它:
具有目标的函数可用于过滤、关联、分组、展平以及其他操作。有关目标操作的完整列表,请参⻅ Kotlin collections reference。

写操作

对于可变集合,还存在可更改集合状态的 写操作 。这些操作包括添加、删除和更新元素。写操作在集合 写操作以及 List 写操作与 Map 写操作的相应部分中列出。
对于某些操作,有成对的函数可以执行相同的操作:一个函数就地应用该操作,另一个函数将结果作为 单独的集合返回。例如,sort() 就地对可变集合进行排序,因此其状态发生了变化;sorted() 创建
一个新集合,该集合包含按排序顺序相同的元素。
    // 将数字直接过滤到新的哈希集中,
    // 从而消除结果中的重复项
    val result = numbers.mapTo(HashSet()) { it.length } println("distinct item lengths are $result")
            val numbers = mutableListOf("one", "two", "three", "four") val sortedNumbers = numbers.sorted()
    println(numbers == sortedNumbers) // false
    numbers.sort()
    println(numbers == sortedNumbers) // true

集合转换

Kotlin 标准库为集合 转换 提供了一组扩展函数。这些函数根据提供的转换规则从现有集合中构建新集 合。在此⻚面中,我们将概述可用的集合转换函数。
映射
映射 转换从另一个集合的元素上的函数结果创建一个集合。基本的映射函数是 map()。它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。结果的顺序与元素的原始顺序相同。如
需应用还要用到元素索引作为参数的转换,请使用 mapIndexed()。
如果转换在某些元素上产生 null 值,则可以通过调用 mapNotNull() 函数取代 map() 或 mapIndexedNotNull() 取代 mapIndexed() 来从结果集中过滤掉 null 值。
映射转换时,有两个选择:转换键,使值保持不变,反之亦然。要将指定转换应用于键,请使用 mapKeys();反过来,mapValues() 转换值。这两个函数都使用将映射条目作为参数的转换,因此可
以操作其键与值。

双路合并

双路合并 转换是根据两个集合中具有相同位置的元素构建配对。在 Kotlin 标准库中,这是通过 zip() 扩展函数完成的。在一个集合(或数组)上以另一个集合(或数组)作为参数调用时,zip() 返回 Pair 对 象 的 列 表( L i s t )。接 收 者 集 合 的 元 素 是 这 些 配 对 中 的 第 一 个 元 素 。如 果 集 合 的 大 小 不 同 ,则
zip() 的结果为较小集合的大小;结果中不包含较大集合的后续元素。zip() 也可以中缀形式调用 a zip b 。
也可以使用带有两个参数的转换函数来调用 zip() :接收者元素和参数元素。在这种情况下,结果 List 包含在具有相同位置的接收者对和参数元素对上调用的转换函数的返回值。

    val numbers = setOf(1, 2, 3)
    println(numbers.map { it * 3 }) println(numbers.mapIndexed { idx, value -> value * idx })
        val numbers = setOf(1, 2, 3)
    println(numbers.mapNotNull { if ( it == 2) null else it * 3 }) println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11) println(numbersMap.mapKeys { it.key.toUpperCase() }) println(numbersMap.mapValues { it.value + it.key.length })
            val colors = listOf("red", "brown", "grey") val animals = listOf("fox", "bear", "wolf") println(colors zip animals)
    val twoAnimals = listOf("fox", "bear") println(colors.zip(twoAnimals))
    val colors = listOf("red", "brown", "grey") val animals = listOf("fox", "bear", "wolf")
    println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"})

当拥有 Pair 的 List 时,可以进行反向转换 unzipping⸺从这些键值对中构建两个列表: — 第一个列表包含原始列表中每个 Pair 的键。
— 第二个列表包含原始列表中每个 Pair 的值。 要分割键值对列表,请调用 unzip()。

关联

关联 转换允许从集合元素和与其关联的某些值构建 Map。在不同的关联类型中,元素可以是关联 Map 中的键或值。
基本的关联函数 associateWith() 创建一个 Map ,其中原始集合的元素是键,并通过给定的转换函 数从中产生值。如果两个元素相等,则仅最后一个保留在 Map 中。
为了使用集合元素作为值来构建 Map,有一个函数 associateBy()。它需要一个函数,该函数根据元 素的值返回键。如果两个元素相等,则仅最后一个保留在 Map 中。还可以使用值转换函数来调用
associateBy() 。
另一种构建 Map 的方法是使用函数 associate(),其中 Map 键和值都是通过集合元素生成的。它需 要一个 lambda 函数,该函数返回 Pair :键和相应 Map 条目的值。
请注意,associate() 会生成临时的 Pair 对象,这可能会影响性能。因此,当性能不是很关键或比 其他选项更可取时,应使用 associate() 。
后者的一个示例:从一个元素一起生成键和相应的值。
此时,首先在一个元素上调用一个转换函数,然后根据该函数结果的属性建立 Pair。

val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4) println(numberPairs.unzip())
    val numbers = listOf("one", "two", "three", "four") println(numbers.associateWith { it.length })
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.associateBy { it.first().toUpperCase() }) println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))
        val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell") println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })

打平

如需操作嵌套的集合,则可能会发现提供对嵌套集合元素进行打平访问的标准库函数很有用。 第一个函数为 flatten()。可以在一个集合的集合(例如,一个 Set 组成的 List )上调用它。该函数
返回嵌套集合中的所有元素的一个 List 。
另一个函数⸺flatMap() 提供了一种灵活的方式来处理嵌套的集合。它需要一个函数将一个集合元 素映射到另一个集合。因此,flatMap() 返回单个列表其中包含所有元素的值。所以,flatMap() 表现为 map()(以集合作为映射结果)与 flatten() 的连续调用。

字符串表示

如果需要以可读格式检索集合内容,请使用将集合转换为字符串的函数:joinToString() 与 joinTo()。
joinToString() 根据提供的参数从集合元素构建单个 String 。joinTo() 执行相同的操作,但 将结果附加到给定的 Appendable 对象。
当使用默认参数调用时,函数返回的结果类似于在集合上调用 toString() :各元素的字符串表示形 式以空格分隔而成的 String 。
要构建自定义字符串表示形式,可以在函数参数 separator、prefix 与 postfix中指定其参数。 结果字符串将以 prefix 开头,以 postfix 结尾。除最后一个元素外,separator 将位于每个元素 之后。

        val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2)) println(numberSets.flatten())
        val containers = listOf( StringContainer(listOf("one", "two", "three")), StringContainer(listOf("four", "five", "six")), StringContainer(listOf("seven", "eight"))
    )
    println(containers.flatMap { it.values })
            val numbers = listOf("one", "two", "three", "four") println(numbers)
    println(numbers.joinToString())
    val listString = StringBuffer("The list of numbers: ") numbers.joinTo(listString)
    println(listString)
        val numbers = listOf("one", "two", "three", "four") println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))

对于较大的集合,可能需要指定 limit ⸺将包含在结果中元素的数量。如果集合大小超出 limit, 所有其他元素将被 truncated 参数的单个值替换。
最后,要自定义元素本身的表示形式,请提供 transform 函数。

        val numbers = (1..100).toList() println(numbers.joinToString(limit = 10, truncated = "<...>"))
    val numbers = listOf("one", "two", "three", "four") println(numbers.joinToString { "Element: ${it.toUpperCase()}"})

过滤

过滤是最常用的集合处理任务之一。在Kotlin中,过滤条件由 谓词 定义⸺接受一个集合元素并且返回 布尔值的 lambda 表达式:true 说明给定元素与谓词匹配,false 则表示不匹配。
标准库包含了一组让你能够通过单个调用就可以过滤集合的扩展函数。这些函数不会改变原始集合,因 此它们既可用于可变集合也可用于只读集合。为了操作过滤结果,应该在过滤后将其赋值给变量或链接 其他函数。
按谓词过滤
基本的过滤函数是 filter()。当使用一个谓词来调用时,filter() 返回与其匹配的集合元素。对于 List 和 Set ,过滤结果都是一个 List ,对 Map 来说结果还是一个 Map 。
filter() 中的谓词只能检查元素的值。如果想在过滤中使用元素在集合中的位置,应该使用 filterIndexed()。它接受一个带有两个参数的谓词:元素的索引和元素的值。
如果想使用否定条件来过滤集合,请使用 filterNot()。它返回一个让谓词产生 false 的元素列表。
还有一些函数能够通过过滤给定类型的元素来缩小元素的类型:
— filterIsInstance() 返回给定类型的集合元素。在一个 List 上被调用 时,filterIsInstance() 返回一个 List,从而让你能够在集合元素上调用 T 类型的 函数。
— filterNotNull() 返回所有的非空元素。在一个 List 上被调用时,filterNotNull() 返回一个 List,从而让你能够将所有元素视为非空对象。

            val numbers = listOf("one", "two", "three", "four") val longerThan3 = numbers.filter { it.length > 3 } println(longerThan3)
    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
    val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} println(filteredMap)
        val numbers = listOf("one", "two", "three", "four")
    val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) }
    val filteredNot = numbers.filterNot { it.length <= 3 }
    println(filteredIdx) println(filteredNot)
        val numbers = listOf(null, 1, "two", 3.0, "four") println("All String elements in upper case:") numbers.filterIsInstance().forEach {
    println(it.toUpperCase()) }
    val numbers = listOf(null, "one", "two", null) numbers.filterNotNull().forEach {
    println(it.length) // 对可空的 String 来说⻓度不可用 }

划分

另一个过滤函数 ‒ partition() ‒ 通过一个谓词过滤集合并且将不匹配的元素存放在一个单独的列 表中。因此,你得到一个 List 的 Pair 作为返回值:第一个列表包含与谓词匹配的元素并且第二个列 表包含原始集合中的所有其他元素。
检验谓词
最后,有些函数只是针对集合元素简单地检测一个谓词:
— 如果至少有一个元素匹配给定谓词,那么 any() 返回 true 。
— 如果没有元素与给定谓词匹配,那么 none() 返回 true 。
— 如果所有元素都匹配给定谓词,那么 all() 返回 true 。注意,在一个空集合上使用任何有效的谓 词去调用 all() 都会返回 true 。这种行为在逻辑上被称为 vacuous truth。
any() 和 none() 也可以不带谓词使用:在这种情况下它们只是用来检查集合是否为空。如果集合 中有元素,any() 返回 true,否则返回 false;none() 则相反。

        val numbers = listOf("one", "two", "three", "four")
    val (match, rest) = numbers.partition { it.length > 3 }
    println(match)
    println(rest)
            val numbers = listOf("one", "two", "three", "four")
    println(numbers.any { it.endsWith("e") }) println(numbers.none { it.endsWith("a") }) println(numbers.all { it.endsWith("e") })
    println(emptyList().all { it > 5 }) // vacuous truth
        val numbers = listOf("one", "two", "three", "four") val empty = emptyList()
    println(numbers.any()) println(empty.any())
    println(numbers.none()) println(empty.none())

plus 与 minus 操作符

在 Kotlin 中,为集合定义了 plus ( + ) 和 minus ( - ) 操作符。它们把一个集合作为第一个操作数;第二
个操作数可以是一个元素或者是另一个集合。返回值是一个新的只读集合: — plus 的结果包含原始集合 和 第二个操作数中的元素。
— minus 的结果包含原始集合中的元素,但第二个操作数中的元素 除外。如果第二个操作数是一个 元素,那么 minus 移除其在原始集合中的 第一次 出现;如果是一个集合,那么移除其元素在原始集 合中的 所有 出现。
有 关 m a p 的 p l u s 和 m i n u s 操 作 符 的 详 细 信 息 ,请 参 ⻅ M a p 相 关 操 作 。也 为 集 合 定 义 了 广 义 赋 值 操作符 plusAssign ( += ) 和 minusAssign ( -= )。然而,对于只读集合,它们实际上使用 plus 或 者 minus 操作符并尝试将结果赋值给同一变量。因此,它们仅在由 var 声明的只读集合中可用。对 于可变集合,如果它是一个 val,那么它们会修改集合。更多详细信息请参⻅集合写操作。

            val numbers = listOf("one", "two", "three", "four")
    val plusList = numbers + "five"
    val minusList = numbers - listOf("three", "four") println(plusList)
    println(minusList)

分组

Kotlin 标准库提供用于对集合元素进行分组的扩展函数。基本函数 groupBy() 使用一个 lambda 函 数并返回一个 Map 。在此 Map 中,每个键都是 lambda 结果,而对应的值是返回此结果的元素 List 。 例如,可以使用此函数将 String 列表按首字母分组。
还可以使用第二个 lambda 参数(值转换函数)调用 groupBy() 。在带有两个 lambda 的 groupBy() 结果 Map 中,由 keySelector 函数生成的键映射到值转换函数的结果,而不是原始元
素。
如果要对元素进行分组,然后一次将操作应用于所有分组,请使用 groupingBy() 函数。它返回一个 Grouping 类型的实例。通过 Grouping 实例,可以以一种惰性的方式将操作应用于所有组:这些分 组实际上是刚好在执行操作前构建的。
即,Grouping 支持以下操作:
— eachCount() 计算每个组中的元素。
— fold() 与 reduce() 对每个组分别执行 fold 与 reduce 操作,作为一个单独的集合并返回结果。
— aggregate() 随后将给定操作应用于每个组中的所有元素并返回结果。这是对 Grouping 执行 任何操作的通用方法。当折叠或缩小不够时,可使用它来实现自定义操作。

            val numbers = listOf("one", "two", "three", "four", "five")
    println(numbers.groupBy { it.first().toUpperCase() })
    println(numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.toUpperCase() }))
            val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.groupingBy { it.first() }.eachCount())

取集合的一部分

Kotlin 标准库包含用于取集合的一部分的扩展函数。这些函数提供了多种方法来选择结果集合的元素: 显式列出其位置、指定结果大小等。

Slice

slice() 返回具有给定索引的集合元素列表。索引既可以是作为区间传入的也可以是作为整数值的集 合传入的。

Take 与 drop

要从头开始获取指定数量的元素,请使用 take() 函数。要从尾开始获取指定数量的元素,请使用 takeLast()。当调用的数字大于集合的大小时,两个函数都将返回整个集合。
要从头或从尾去除给定数量的元素,请调用 drop() 或 dropLast() 函数。
还可以使用谓词来定义要获取或去除的元素的数量。有四个与上述功能相似的函数:
— takeWhile() 是带有谓词的 take() :它将不停获取元素直到排除与谓词匹配的首个元素。如果 首个集合元素与谓词匹配,则结果为空。
— takeLastWhile() 与 takeLast() 类似:它从集合末尾获取与谓词匹配的元素区间。区间的首 个元素是与谓词不匹配的最后一个元素右边的元素。如果最后一个集合元素与谓词匹配,则结果为
空。
— dropWhile() 与具有相同谓词的 takeWhile() 相反:它将首个与谓词不匹配的元素返回到末
尾。
— dropLastWhile() 与具有相同谓词的 takeLastWhile() 相反:它返回从开头到最后一个与谓
词不匹配的元素。

    val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.slice(1..3))
    println(numbers.slice(0..4 step 2))
    println(numbers.slice(setOf(3, 5, 0)))
        val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.take(3))
    println(numbers.takeLast(3))
    println(numbers.drop(1))
    println(numbers.dropLast(5))
            val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.takeWhile { !it.startsWith('f') }) println(numbers.takeLastWhile { it != "three" }) println(numbers.dropWhile { it.length == 3 }) println(numbers.dropLastWhile { it.contains('i') })

Chunked

要将集合分解为给定大小的“块”,请使用 chunked() 函数。chunked() 采用一个参数(块的大小), 并返回一个 List 其中包含给定大小的 List 。第一个块从第一个元素开始并包含 size 元素,第二 个块包含下一个 size 元素,依此类推。最后一个块的大小可能较小。
还可以立即对返回的块应用转换。为此,请在调用 chunked() 时将转换作为 lambda 函数提供。 lambda参数是集合的一块。当通过转换调用 chunked() 时,这些块是临时的 List,应立即在该 lambda 中使用。

Windowed

可以检索给定大小的集合元素中所有可能区间。获取它们的函数称为 windowed():它返回一个元素 区间列表,比如通过给定大小的滑动窗口查看集合,则会看到该区间。与 chunked() 不 同,windowed() 返回从每个集合元素开始的元素区间(窗口)。所有窗口都作为单个 List 的元素返 回。
windowed() 通过可选参数提供更大的灵活性:
— step 定义两个相邻窗口的第一个元素之间的距离。默认情况下,该值为 1,因此结果包含从所有元 素开始的窗口。如果将 step 增加到 2,将只收到以奇数元素开头的窗口:第一个、第三个等。
— partialWindows包含从集合末尾的元素开始的较小的窗口。例如,如果请求三个元素的窗口,就 不能为最后两个元素构建它们。在本例中,启用 partialWindows 将包括两个大小为2与1的列表。
最后,可以立即对返回的区间应用转换。为此,在调用 windowed() 时将转换作为 lambda 函数提供。
要 构 建 两 个 元 素 的 窗 口 ,有 一 个 单 独 的 函 数 ⸺ z i p W i t h N e x t ( ) 。它 创 建 接 收 器 集 合 的 相 邻 元 素 对 。 请注意,zipWithNext() 不会将集合分成几对;它为 每个 元素创建除最后一个元素外的对,因此它 在 [1, 2, 3, 4] 上的结果为 [[1, 2], [2, 3], [3, 4]] ,而不是 [[1, 2 ],[3, 4]] 。
zipWithNext() 也可以通过转换函数来调用;它应该以接收者集合的两个元素作为参数。

        val numbers = (0..13).toList() println(numbers.chunked(3))
        val numbers = (0..13).toList()
    println(numbers.chunked(3) { it.sum() }) // `it` 为原始集合的一个块
        val numbers = listOf("one", "two", "three", "four", "five") println(numbers.windowed(3))
        val numbers = (1..10).toList()
    println(numbers.windowed(3, step = 2, partialWindows = true)) println(numbers.windowed(3) { it.sum() })
  val numbers = listOf("one", "two", "three", "four", "five") println(numbers.zipWithNext())
    println(numbers.zipWithNext() { s1, s2 -> s1.length > s2.length})

取单个元素

Kotlin 集合提供了一套从集合中检索单个元素的函数。此⻚面描述的函数适用于 list 和 set。
正如 list 的定义所言,list 是有序集合。因此,list 中的每个元素都有其位置可供你引用。除了此⻚面上 描述的函数外,list 还提供了更广泛的一套方法去按索引检索和搜索元素。有关更多详细信息,请参⻅ List 相关操作。
反过来,从定义来看,set 并不是有序集合。但是,Kotlin 中的 Set 按某些顺序存储元素。这些可以是插 入顺序(在 LinkedHashSet 中)、自然排序顺序(在 SortedSet 中)或者其他顺序。一组元素的顺序
也可以是未知的。在这种情况下,元素仍会以某种顺序排序,因此,依赖元素位置的函数仍会返回其结 果。但是,除非调用者知道所使用的 Set 的具体实现,否则这些结果对于调用者是不可预测的。

按位置取

为了检索特定位置的元素,有一个函数 elementAt()。用一个整数作为参数来调用它,你会得到给定 位置的集合元素。第一个元素的位置是 0 ,最后一个元素的位置是 (size - 1) 。
elementAt() 对于不提供索引访问或非静态已知提供索引访问的集合很有用。在使用 List 的情况 下,使用索引访问操作符(get() 或 [])更为习惯。
还有一些有用的别名来检索集合的第一个和最后一个元素:first() 和 last()。
为了避免在检索位置不存在的元素时出现异常,请使用 elementAt() 的安全变体: — 当指定位置超出集合范围时,elementAtOrNull()返回null。
— elementAtOrElse() 还接受一个 lambda 表达式,该表达式能将一个 Int 参数映射为一个集合 元素类型的实例。当使用一个越界位置来调用时,elementAtOrElse() 返回对给定值调用该 lambda 表达式的结果。

按条件取

                    val numbers = linkedSetOf("one", "two", "three", "four", "five") println(numbers.elementAt(3))
    val numbersSortedSet = sortedSetOf("one", "two", "three", "four") println(numbersSortedSet.elementAt(0)) // 元素以升序存储
    val numbers = listOf("one", "two", "three", "four", "five") println(numbers.first())
    println(numbers.last())
        val numbers = listOf("one", "two", "three", "four", "five") println(numbers.elementAtOrNull(5))
    println(numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"})

函数 first() 和 last() 还可以让你在集合中搜索与给定谓词匹配的元素。当你使用测试集合元素 的谓词调用 first() 时,你会得到对其调用谓词产生 true 的第一个元素。反过来,带有一个谓词的
last() 返回与其匹配的最后一个元素。
如果没有元素与谓词匹配,两个函数都会抛异常。为了避免它们,请改用 firstOrNull() 和 lastOrNull():如果找不到匹配的元素,它们将返回 null 。
或者,如果别名更适合你的情况,那么可以使用别名: — 使用 find() 代替 firstOrNull()
— 使用 findLast() 代替 lastOrNull()

随机取元素

如果需要检索集合的一个随机元素,那么请调用 random() 函数。你可以不带参数或者使用一个 Random 对象作为随机源来调用它。
检测存在与否
如需检查集合中某个元素的存在,可以使用 contains() 函数。如果存在一个集合元素等于 ( equals() )函数参数,那么它返回 true 。你可以使用 in 关键字以操作符的形式调用
contains() 。
如需一次检查多个实例的存在,可以使用这些实例的集合作为参数调用 containsAll()。
此外,你可以通过调用 isEmpty() 和 isNotEmpty() 来检查集合中是否包含任何元素。
        val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.first { it.length > 3 })
    println(numbers.last { it.startsWith("f") })
        val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.firstOrNull { it.length > 6 })
        val numbers = listOf(1, 2, 3, 4) println(numbers.find { it % 2 == 0 }) println(numbers.findLast { it % 2 == 0 })
    val numbers = listOf(1, 2, 3, 4) println(numbers.random())
        val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.contains("four"))
    println("zero" in numbers)
    println(numbers.containsAll(listOf("four", "two"))) println(numbers.containsAll(listOf("one", "zero")))
    
    val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.isEmpty())
    println(numbers.isNotEmpty())
    val empty = emptyList() println(empty.isEmpty()) println(empty.isNotEmpty())

集合排序

元素的顺序是某些集合类型的一个重要方面。例如,如果拥有相同元素的两个列表的元素顺序不同,那 么这两个列表也不相等。

在 Kotlin 中,可以通过多种方式定义对象的顺序。
首先,有 自然 顺序。它是为 Comparable 接口的继承者定义的。当没有指定其他顺序时,使用自然顺序 为它们排序。
大多数内置类型是可比较的:
— 数值类型使用传统的数值顺序:1 大于 0 ;-3.4f 大于 -5f ,以此类推。
— Char 和 String 使用字典顺序:b 大于 a ;world 大于 hello 。
如需为用戶定义的类型定义一个自然顺序,可以让这个类型继承 Comparable 。这需要实现 compareTo() 函数。compareTo() 必须将另一个具有相同类型的对象作为参数并返回一个整数值
来显示哪个对象更大:
— 正值表明接收者对象更大。 — 负值表明它小于参数。
— 0说明对象相等。
下面是一个类,可用于排序由主版本号和次版本号两部分组成的版本。
                    class Version(val major: Int, val minor: Int): Comparable { override fun compareTo(other: Version): Int {
    if (this.major != other.major) { return this.major - other.major
    } else if (this.minor != other.minor) { return this.minor - other.minor
    } else return 0 }
    }
    fun main() {
    println(Version(1, 2) > Version(1, 3)) println(Version(2, 0) > Version(1, 5))
    }

自定义 顺序

让你可以按自己喜欢的方式对任何类型的实例进行排序。特别是,你可以为不可比较类型 定义顺序,或者为可比较类型定义非自然顺序。如需为类型定义自定义顺序,可以为其创建一个 Comparator。Comparator 包含 compare() 函数:它接受一个类的两个实例并返回它们之间比较
的整数结果。如上所述,对结果的解释与 compareTo() 的结果相同。

        val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
    println(listOf("aaa", "bb", "c").sortedWith(lengthComparator))

有了 lengthComparator,你可以按照字符串的⻓度而不是默认的字典顺序来排列字符串。
定义一个 Comparator 的一种比较简短的方式是标准库中的 compareBy() 函数。compareBy() 接受一个 lambda 表达式,该表达式从一个实例产生一个 Comparable 值,并将自定义顺序定义为生 成值的自然顺序。使用 compareBy(),上面示例中的⻓度比较器如下所示:
Kotlin 集合包提供了用于按照自然顺序、自定义顺序甚至随机顺序对集合排序的函数。在此⻚面上,我 们将介绍适用于只读集合的排序函数。这些函数将它们的结果作为一个新集合返回,集合里包含了按照 请 求 顺 序 排 序 的 来 自 原 始 集 合 的 元 素 。如 果 想 学 习 就 地 对 可 变 集 合 排 序 的 函 数 ,请 参 ⻅ L i s t 相 关 操 作 。

自然顺序

基本的函数 sorted() 和 sortedDescending() 返回集合的元素,这些元素按照其自然顺序升序和 降序排序。这些函数适用于 Comparable 元素的集合。

自定义顺序

为了按照自定义顺序排序或者对不可比较对象排序,可以使用函数 sortedBy() 和 sortedByDescending()。它们接受一个将集合元素映射为 Comparable 值的选择器函数,并以该 值的自然顺序对集合排序。
如需为集合排序定义自定义顺序,可以提供自己的 Comparator 。为此,调用传入 Comparator 的 sortedWith() 函数。使用此函数,按照字符串⻓度排序如下所示:
###倒序
你可以使用 reversed() 函数以相反的顺序检索集合。
        tln(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length }))
        val numbers = listOf("one", "two", "three", "four")
    println("Sorted ascending: ${numbers.sorted()}") println("Sorted descending: ${numbers.sortedDescending()}")
        val numbers = listOf("one", "two", "three", "four")
    val sortedNumbers = numbers.sortedBy { it.length } println("Sorted by length ascending: $sortedNumbers")
    val sortedByLast = numbers.sortedByDescending { it.last() } println("Sorted by the last letter descending: $sortedByLast")
        val numbers = listOf("one", "two", "three", "four")
    println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}")
    val numbers = listOf("one", "two", "three", "four") println(numbers.reversed())

reversed() 返回带有元素副本的新集合。因此,如果你之后改变了原始集合,这并不会影响先前获 得的 reversed() 的结果。
另 一 个 反 向 函 数 ⸺ a s R e v e r s e d ( ) ⸺ 返 回 相 同 集 合 实 例 的 一 个 反 向 视 图 ,因 此 ,如 果 原 始 列 表 不 会 发生变化,那么它会比 reversed() 更轻量,更合适。
如果原始列表是可变的,那么其所有更改都会反映在其反向视图中,反之亦然。
但是,如果列表的可变性未知或者源根本不是一个列表,那么 reversed() 更合适,因为其结果是一 个未来不会更改的副本。

随机顺序

最后,shuffled() 函数返回一个包含了以随机顺序排序的集合元素的新的 List 。你可以不带参数 或者使用 Random 对象来调用它。

        val numbers = listOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers)
    val numbers = mutableListOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers)
    numbers.add("five")
    println(reversedNumbers)
        val numbers = listOf("one", "two", "three", "four") println(numbers.shuffled())

集合聚合操作

Kotlin 集合包含用于常用的 聚合操作(基于集合内容返回单个值的操作)的函数 。其中大多数是众所 周知的,并且其工作方式与在其他语言中相同。
— min() 与 max() 分别返回最小和最大的元素;
— average() 返回数字集合中元素的平均值;
— sum() 返回数字集合中元素的总和;
— count() 返回集合中元素的数量;
还有一些通过某些选择器函数或自定义 Comparator 来检索最小和最大元素的函数。
— maxBy()/minBy() 接受一个选择器函数并返回使选择器返回最大或最小值的元素。
— maxWith()/minWith() 接受一个 Comparator 对象并且根据此 Comparator 对象返回最大 或最小元素。
此外,有一些高级的求和函数,它们接受一个函数并返回对所有元素调用此函数的返回值的总和: — sumBy() 使用对集合元素调用返回 Int 值的函数。
— sumByDouble() 与返回 Double 的函数一起使用。
##Fold 与 reduce
对于更特定的情况,有函数 reduce() 和 fold(),它们依次将所提供的操作应用于集合元素并返回累 积的结果。操作有两个参数:先前的累积值和集合元素。

        val numbers = listOf(6, 42, 10, 4)
    println("Count: ${numbers.count()}") println("Max: ${numbers.max()}") println("Min: ${numbers.min()}") println("Average: ${numbers.average()}") println("Sum: ${numbers.sum()}")
            val numbers = listOf(5, 42, 10, 4)
    val min3Remainder = numbers.minBy { it % 3 } println(min3Remainder)
    val strings = listOf("one", "two", "three", "four")
    val longestString = strings.maxWith(compareBy { it.length }) println(longestString)
        val numbers = listOf(5, 42, 10, 4) println(numbers.sumBy { it * 2 }) println(numbers.sumByDouble { it.toDouble() / 2 })

这两个函数的区别在于:fold() 接受一个初始值并将其用作第一步的累积值,而 reduce() 的第一 步则将第一个和第二个元素作为第一步的操作参数。

    val numbers = listOf(5, 2, 10, 4)
    val sum = numbers.reduce { sum, element -> sum + element } println(sum)
    val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 } println(sumDoubled)
    //val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 } //错误:第一个 元素在结果中没有加倍
    //println(sumDoubledReduce)

上面的实例展示了区别:fold() 用于计算加倍的元素之和。如果将相同的函数传给 reduce(),那 么它会返回另一个结果,因为在第一步中它将列表的第一个和第二个元素作为参数,所以第一个元素不
会被加倍。
如需将函数以相反的顺序应用于元素,可以使用函数 reduceRight() 和 foldRight() 它们的工作
方式类似于 fold() 和 reduce(),但从最后一个元素开始,然后再继续到前一个元素。记住,在使用
foldRight 或 reduceRight 时,操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积 值。
你还可以使用将元素索引作为参数的操作。为此,使用函数 reduceIndexed() 和 foldIndexed() 传递元素索引作为操作的第一个参数。
最后,还有将这些操作从右到左应用于集合元素的函数⸺reduceRightIndexed() 与 foldRightIndexed()。

        val numbers = listOf(5, 2, 10, 4)
    val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 } println(sumDoubledRight)
        val numbers = listOf(5, 2, 10, 4)
    val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum }
    println(sumEven)
    val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
    println(sumEvenRight)

集合写操作

可变集合支持更改集合内容的操作,例如添加或删除元素。在此⻚面上,我们将描述实现 MutableCollection 的所有写操作。有关 List 与 Map 可用的更多特定操作,请分别参⻅ List 相
关操作与 Map 相关操作。 添加元素
要将单个元素添加到列表或集合,请使用 add() 函数。指定的对象将添加到集合的末尾。
addAll() 将参数对象的每个元素添加到列表或集合中。参数可以是 Iterable 、Sequence 或 Array 。接收者的类型和参数可能不同,例如,你可以将所有内容从 Set 添加到 List 。
当在列表上调用时,addAll() 会按照在参数中出现的顺序添加各个新元素。你也可以调用 addAll() 时指定一个元素位置作为第一参数。参数集合的第一个元素会被插入到这个位置。其他元
素将跟随在它后面,将接收者元素移到末尾。
你还可以使用 plus 运算符 - plusAssign ( += ) 添加元素。当应用于可变集合时,+= 将第二个操作 数(一个元素或另一个集合)追加到集合的末尾。
##删除元素
若要从可变集合中移除元素,请使用 remove() 函数。remove() 接受元素值,并删除该值的一个匹 配项。
要一次删除多个元素,有以下函数:
— removeAll() 移除参数集合中存在的所有元素。或者,你可以用谓词作为参数来调用它;在这种情

            val numbers = mutableListOf(1, 2, 3, 4) numbers.add(5)
    println(numbers)
            val numbers = mutableListOf(1, 2, 5, 6) numbers.addAll(arrayOf(7, 8)) println(numbers)
    numbers.addAll(2, setOf(3, 4)) println(numbers)
        val numbers = mutableListOf("one", "two") numbers += "three"
    println(numbers)
    numbers += listOf("four", "five") println(numbers)
    val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.remove(3) // 删除了第一个 `3` println(numbers)
    numbers.remove(5) // 什么都没删除 println(numbers)

况下,函数移除谓词产生 true 的所有元素。
— r e t a i n A l l ( ) 与 r e m o v e A l l ( ) 相 反 :它 移 除 除 参 数 集 合 中 的 元 素 之 外 的 所 有 元 素 。当 与 谓 词 一 起使用时,它只留下与之匹配的元素。
— clear() 从列表中移除所有元素并将其置空。

        val numbers = mutableListOf(1, 2, 3, 4) println(numbers)
    numbers.retainAll { it >= 3 } println(numbers)
    numbers.clear()
    println(numbers)
    val numbersSet = mutableSetOf("one", "two", "three", "four") numbersSet.removeAll(setOf("one", "two")) println(numbersSet)

从集合中移除元素的另一种方法是使用 minusAssign ( -= ) ⸺原地修改版的 minus 操作符。
minus 操作符。第二个参数可以是元素类型的单个实例或另一个集合。右边是单个元素时,-= 会移除
它的第一个匹配项。反过来,如果它是一个集合,那么它的所有元素的每次出现都会删除。例如,如果列 表包含重复的元素,它们将被同时删除。第二个操作数可以包含集合中不存在的元素。这些元素不会影 响操作的执行。

更新元素

list 和 map 还提供更新元素的操作。它们在 List 相关操作与 Map 相关操作中有所描述。对于 set 来 说,更新没有意义,因为它实际上是移除一个元素并添加另一个元素。

        val numbers = mutableListOf("one", "two", "three", "three", "four") numbers -= "three"
    println(numbers)
    numbers -= listOf("four", "five")
    //numbers -= listOf("four") // 与上述相同 println(numbers)

List 相关操作

List 是 Kotlin 标准库中最受欢迎的集合类型。对列表元素的索引访问为 List 提供了一组强大的操作。 按索引取元素
List 支持按索引取元素的所有常用操作:elementAt() 、first() 、last() 与取单个元素中列 出的其他操作。List 的特点是能通过索引访问特定元素,因此读取元素的最简单方法是按索引检索它。
这是通过 get() 函数或简写语法 [index] 来传递索引参数完成的。
如果 List ⻓度小于指定的索引,则抛出异常。另外,还有两个函数能避免此类异常:
— getOrElse() 提供用于计算默认值的函数,如果集合中不存在索引,则返回默认值。 — getOrNull() 返回 null 作为默认值。
###取列表的一部分
除了取集合的一部分中常用的操作,List 还提供 subList() 该函数将指定元素范围的视图作为列表 返回。因此,如果原始集合的元素发生变化,则它在先前创建的子列表中也会发生变化,反之亦然。
查找元素位置 线性查找
在任何列表中,都可以使用 indexOf() 或 lastIndexOf() 函数找到元素的位置。它们返回与列表 中给定参数相等的元素的第一个或最后一个位置。如果没有这样的元素,则两个函数均返回 -1 。
还有一对函数接受谓词并搜索与之匹配的元素:
— indexOfFirst() 返回与谓词匹配的第一个元素的索引,如果没有此类元素,则返回 -1 。
— indexOfLast() 返回与谓词匹配的最后一个元素的索引,如果没有此类元素,则返回 -1 。

            val numbers = listOf(1, 2, 3, 4) println(numbers.get(0)) println(numbers[0]) //numbers.get(5) println(numbers.getOrNull(5)) println(numbers.getOrElse(5, {it}))
    // exception!
    // null
    // 5
    val numbers = (0..13).toList() println(numbers.subList(3, 6))
        val numbers = listOf(1, 2, 3, 4, 2, 5) println(numbers.indexOf(2)) println(numbers.lastIndexOf(2))
  val numbers = mutableListOf(1, 2, 3, 4) println(numbers.indexOfFirst { it > 2}) println(numbers.indexOfLast { it % 2 == 1})

在有序列表中二分查找

还 有 另 一 种 搜 索 列 表 中 元 素 的 方 法 ⸺ 二 分 查 找 算 法 。它 的 工 作 速 度 明 显 快 于 其 他 内 置 搜 索 功 能 ,但 要 求该列表按照一定的顺序(自然排序或函数参数中提供的另一种排序)按升序排序过。否则,结果是不确 定的。
要搜索已排序列表中的元素,请调用 binarySearch() 函数,并将该值作为参数传递。如果存在这样 的元素,则函数返回其索引;否则,将返回 (-insertionPoint - 1),其中 insertionPoint 为 应插入此元素的索引,以便列表保持排序。如果有多个具有给定值的元素,搜索则可以返回其任何索引。
还可以指定要搜索的索引区间:在这种情况下,该函数仅在两个提供的索引之间搜索。
###Comparator 二分搜索
如果列表元素不是 Comparable ,则应提供一个用于二分搜索的 Comparator。该列表必须根据此
Comparator 以升序排序。来看一个例子:
这是一个不可 排序 的 Product 实例列表,以及一个定义排序的 Comparator :如果 p1 的价格小于 p2 的价格,则产品 p1 在产品 p2 之前。因此,按照此顺序对列表进行升序排序后,使用 binarySearch() 查找指定的 Product 的索引。
当列表使用与自然排序不同的顺序时(例如,对 String 元素不区分大小写的顺序),自定义 Comparator 也很方便。

比较函数二分搜索

        val numbers = mutableListOf("one", "two", "three", "four") numbers.sort()
    println(numbers)
    println(numbers.binarySearch("two")) // 3 println(numbers.binarySearch("z")) // -5 println(numbers.binarySearch("two", 0, 2)) // -3
        val productList = listOf( Product("WebStorm", 49.0), Product("AppCode", 99.0), Product("DotTrace", 129.0), Product("ReSharper", 149.0))
    println(productList.binarySearch(Product("AppCode", 99.0), compareBy { it.price }.thenBy { it.name }))
            val colors = listOf("Blue", "green", "ORANGE", "Red", "yellow") println(colors.binarySearch("RED", String.CASE_INSENSITIVE_ORDER)) // 3

使用 比较 函数的二分搜索无需提供明确的搜索值即可查找元素。取而代之的是,它使用一个比较函数 将元素映射到 Int 值,并搜索函数返回 0 的元素。该列表必须根据提供的函数以升序排序;换句话说,
比较的返回值必须从一个列表元素增⻓到下一个列表元素。

    data class Product(val name: String, val price: Double)
    fun priceComparison(product: Product, price: Double) = sign(product.price - price).toInt()
    fun main() {
    val productList = listOf(
    Product("WebStorm", 49.0), Product("AppCode", 99.0), Product("DotTrace", 129.0), Product("ReSharper", 149.0))
    println(productList.binarySearch { priceComparison(it, 99.0) }) }
        Comparator 与比较函数二分搜索都可以针对列表区间执行。

List 写操作

除了集合写操作中描述的集合修改操作之外,可变列表还支持特定的写操作。这些操作使用索引来访问 元素以扩展列表修改功能。

添加

要将元素添加到列表中的特定位置,请使用 add() 或 addAll() 并提供元素插入的位置作为附加参 数。位置之后的所有元素都将向右移动。
####更新
列表还提供了在指定位置替换元素的函数⸺set() 及其操作符形式 [] 。set() 不会更改其他元素 的索引。
fill() 简单地将所有集合元素的值替换为指定值。
        val numbers = mutableListOf("one", "five", "six") numbers.add(1, "two")
    numbers.addAll(2, listOf("three", "four")) println(numbers)
        val numbers = mutableListOf("one", "five", "three") numbers[1] = "two"
    println(numbers)
    val numbers = mutableListOf(1, 2, 3, 4) numbers.fill(3)
    println(numbers)

删除

要从列表中删除指定位置的元素,请使用 removeAt() 函数,并将位置作为参数。在元素被删除之后出 现的所有元素索引将减 1。

排序

在集合排序中,描述了按特定顺序检索集合元素的操作。对于可变列表,标准库中提供了类似的扩展函 数,这些扩展函数可以执行相同的排序操作。将此类操作应用于列表实例时,它将更改指定实例中元素 的顺序。
就地排序函数的名称与应用于只读列表的函数的名称相似,但没有 ed/d 后缀:
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.removeAt(1)
println(numbers)

— —
中的更改将反映在原始列表中。以下示例展示了可变列表的排序函数:
sort* 在所有排序函数的名称中代替 sorted* :sort()、sortDescending()、sortBy() 等 等。
shuffle() 代替 shuffled() 。
reverse() 代替 reversed() 。
asReversed() 在可变列表上调用会返回另一个可变列表,该列表是原始列表的反向视图。在该视图

        val numbers = mutableListOf("one", "two", "three", "four")
    numbers.sort()
    println("Sort into ascending: $numbers") numbers.sortDescending()
    println("Sort into descending: $numbers")
    numbers.sortBy { it.length }
    println("Sort into ascending by length: $numbers") numbers.sortByDescending { it.last() }
    println("Sort into descending by the last letter: $numbers")
    numbers.sortWith(compareBy { it.length }.thenBy { it }) println("Sort by Comparator: $numbers")
    numbers.shuffle() println("Shuffle: $numbers")
    numbers.reverse() println("Reverse: $numbers")

Set 相关操作

Kotlin 集合包中包含 set 常用操作的扩展函数:查找交集、并集或差集。
要将两个集合合并为一个(并集),可使用 union() 函数。也能以中缀形式使用 a union b 。注意,对 于有序集合,操作数的顺序很重要:在结果集合中,左侧操作数在前。
要查找两个集合中都存在的元素(交集),请使用 intersect() 。要查找另一个集合中不存在的集合元 素(差集),请使用 subtract() 。这两个函数也能以中缀形式调用,例如,a intersect b 。
注意,List 也支持 Set 操作。但是,对 List 进行 Set 操作的结果仍然是 Set ,因此将删除所有重复的 元素。

        val numbers = setOf("one", "two", "three") println(numbers union setOf("four", "five"))
    println(setOf("four", "five") union numbers)
    println(numbers intersect setOf("two", "one")) println(numbers subtract setOf("three", "four")) println(numbers subtract setOf("four", "three")) // 相同的输出

Map 相关操作

在 map 中,键和值的类型都是用戶定义的。对基于键的访问启用了各种特定于 map 的处理函数,从键 获取值到对键和值进行单独过滤。在此⻚面上,我们提供了来自标准库的 map 处理功能的描述。

取键与值

要从 Map 中检索值,必须提供其键作为 get() 函数的参数。还支持简写 [key] 语法。如果找不到给 定的键,则返回 null 。还有一个函数 getValue() ,它的行为略有不同:如果在 Map 中找不到键,则 抛出异常。此外,还有两个选项可以解决键缺失的问题:
— getOrElse() 与 list 的工作方式相同:对于不存在的键,其值由给定的 lambda 表达式返回。
— getOrDefault() 如果找不到键,则返回指定的默认值。
要对 map 的所有键或所有值执行操作,可以从属性 keys 和 values 中相应地检索它们。keys 是 Map 中所有键的集合,values 是 Map 中所有值的集合。
###过滤
可以使用 filter() 函数来过滤 map 或其他集合。对 map 使用 filter() 函数时,Pair 将作为 参数的谓词传递给它。它将使用谓词同时过滤其中的键和值。
还有两种用于过滤 map 的特定函数:按键或按值。这两种方式,都有对应的函数:filterKeys() 和 filterValues() 。两者都将返回一个新 Map ,其中包含与给定谓词相匹配的条目。
filterKeys() 的谓词仅检查元素键,filterValues() 的谓词仅检查值。

            val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.get("one"))
    println(numbersMap["one"]) println(numbersMap.getOrDefault("four", 10)) println(numbersMap["five"]) // null //numbersMap.getValue("six") // exception!
        val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.keys)
    println(numbersMap.values)
        val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
    val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} println(filteredMap)
        val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11) val filteredKeysMap = numbersMap.filterKeys { it.endsWith("1") }
    val filteredValuesMap = numbersMap.filterValues { it < 10 }
    println(filteredKeysMap) println(filteredValuesMap)
    plus 与 minus 操作

由于需要访问元素的键,plus( + )与 minus( - )运算符对 map 的作用与其他集合不同。plus 返回 包 含 两 个 操 作 数 元 素 的 M a p :左 侧 的 M a p 与 右 侧 的 P a i r 或 另 一 个 M a p 。当 右 侧 操 作 数 中 有 左 侧
Map 中已存在的键时,该条目将使用右侧的值。
minus 将根据左侧 Map 条目创建一个新 Map ,右侧操作数带有键的条目将被剔除。因此,右侧操作 数可以是单个键或键的集合:list 、set 等。
关于在可变 Map 中使用 plusAssign( += )与 minusAssign( -= )运算符的详细信息,请参⻅ Map 写操作 。

Map 写操作

>Mutable Map(可变 Map )提供特定的 Map 写操作。这些操作使你可以使用键来访问或更改 Map 值。 Map 写操作的一些规则:
— 值可以更新。反过来,键也永远不会改变:添加条目后,键是不变的。 — 每个键都有一个与之关联的值。也可以添加和删除整个条目。
下面是对可变 Map 中可用写操作的标准库函数的描述。

添加与更新条目

要将新的键值对添加到可变 Map ,请使用 put() 。将新条目放入 LinkedHashMap(Map的默认实 现)后,会添加该条目,以便在 Map 迭代时排在最后。在 Map 类中,新元素的位置由其键顺序定义。
要一次添加多个条目,请使用 putAll() 。它的参数可以是 Map 或一组 Pair :Iterable 、 Sequence 或 Array 。
如果给定键已存在于 Map 中,则 put() 与 putAll() 都将覆盖值。因此,可以使用它们来更新 Map 条目的值。
            val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap + Pair("four", 4))
    println(numbersMap + Pair("one", 10))
    println(numbersMap + mapOf("five" to 5, "one" to 11))
        val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap - "one")
    println(numbersMap - listOf("two", "four"))
            val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap.put("three", 3)
    println(numbersMap)
        val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.putAll(setOf("four" to 4, "five" to 5)) println(numbersMap)

    val numbersMap = mutableMapOf("one" to 1, "two" to 2)
    val previousValue = numbersMap.put("one", 11)
    println("value associated with 'one', before: $previousValue, after: ${numbersMap["one"]}")
    println(numbersMap)

还可以使用快速操作符将新条目添加到 Map 。有两种方式: — plusAssign( += )操作符。
— [] 操作符为 put() 的别名。
使用 Map 中存在的键进行操作时,将覆盖相应条目的值。

删除条目

要从可变 Map 中删除条目,请使用 remove() 函数。调用 remove() 时,可以传递键或整个键值对。 如果同时指定键和值,则仅当键值都匹配时,才会删除此的元素。

还可以通过键或值从可变 Map 中删除条目。在 Map 的 .keys 或 .values 中调用 remove() 并 提供键或值来删除条目。在 .values 中调用时,remove() 仅删除给定值匹配到的的第一个条目。
minusAssign( -= )操作符也可用于可变 Map 。
    val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap["three"] = 3 // 调用 numbersMap.put("three", 3) numbersMap += mapOf("four" to 4, "five" to 5) println(numbersMap)
    
    val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.remove("one")
    println(numbersMap)
    numbersMap.remove("three", 4) //不会删除任何条目 println(numbersMap)
        
    val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3, "threeAgain" to 3) numbersMap.keys.remove("one")
    println(numbersMap)
    numbersMap.values.remove(3)
    println(numbersMap)
    
    val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap -= "two"
    println(numbersMap)
    numbersMap -= "five" //不会删除任何条目 println(numbersMap)

你可能感兴趣的:(Kotlin系列之四-集合)