Kotlin 标准库提供了基本集合类型的实现: set、list 以及 map。 一对接口代表每种集合类型:
//只读 list val list1 = listOf<String>() //创建空集合时必须指定类型 val list2 = listOf("a", "b") // List //可变 list val list1 = mutableListOf<String>() //ArrayList val list2 = mutableListOf("a", "b") //只读 set val set1 = setOf<String>() // EmptySet val set2 = setOf("a", "b") //LinkedHashSet //可变 set val mutableSetOf = mutableSetOf<String>() // LinkedHashSet val mutableSetOf1 = mutableSetOf("a", "b") //只读 map val mapOf = mapOf<String, String>() // EmptyMap val mapOf1 = mapOf("1" to "a", "2" to "b") // LinkedHashMap //可变 map val mutableMapOf = mutableMapOf<String, String>() // LinkedHashMap val mutableMapOf1 = mutableMapOf("1" to "a", "2" to "b") // LinkedHashMap
注意:to 符号创建了一个短时存活的Pair对象,因此建议仅在性能不重要时才使用它。 为避免过多的内存使用,请使用其他方法。例如,可以创建可写 Map 并使用写入操作填充它。apply()函数可以帮助保持初始化流畅。
val numbersMap = mutableMapOf<String, String>().apply { this["one"] = "1"; this["two"] = "2" }
注:setOf、mutableSetOf返回的集合中的元素按指定的顺序进行迭代的。具有相同元素的集合无论顺序如何都是相等的。
val set1 = setOf(1, 2, 3) val set2 = setOf(3, 2, 1) // setOf保留元素的迭代顺序 println(set1) // [1, 2, 3] println(set2) // [3, 2, 1] // 具有相同元素的集合无论顺序如何都是相等的 println("set1 == set2 is ${set1 == set2}") // true
val emptyList = emptyList<String>() //EmptyList val emptySet = emptySet<String>() // EmptySet val emptyMap = emptyMap<String, String>() // EmptyMap
对于 List,有一个接受 List 的大小与初始化函数的构造函数,该初始化函数根据索引定义元素的值。
//都是可变集合 返回的都是ArrayList val list = List(3) { it * 2 } val mutableList = MutableList(3) { it * 2 }
要创建具体类型的集合,例如ArrayList或LinkedList,可以使用这些类型的构造函数。 类似的构造函数对于Set与Map的各实现中均有提供。
val linkedList = LinkedList<String>() val linkedList1 = LinkedList(listOf("one", "two", "three")) val arrayList = ArrayList<String>() val linkedHashSet = LinkedHashSet<String>() val linkedHashMap = LinkedHashMap<String, String>()
要创建与现有集合具有相同元素的集合,可以使用复制操作。标准库中的集合复制操作创建了具有相同元素引用的浅复制集合。 因此,对集合元素属性所做的更改会反映在其所有副本中。
在特定时刻通过集合复制函数,例如toList()、toMutableList()、toSet()等等,创建了集合的快照,结果是创建了一个具有相同元素的新集合。如果在源集合中添加或删除元素,则不会影响副本。副本也可以独立于源集合进行更改。
val sourceList = mutableListOf(People("李雷"), People("韩梅梅"), People("汤姆")) val copyList = sourceList.toMutableList() val readOnlyCopyList = sourceList.toList() sourceList.add(People("杰瑞")) // 源集合添加元素,不影响复制所得集合 //readOnlyCopyList.add(People("杰瑞")) // 只读集合不能添加,会编译保存 Log.d("TAG","$sourceList") // [People(name=李雷), People(name=韩梅梅), People(name=汤姆), People(name=杰瑞)] Log.d("TAG","$copyList") //[People(name=李雷), People(name=韩梅梅), People(name=汤姆)] sourceList[1].name = "周杰伦" // 源集合修改元素属性,会影响复制所得集合 Log.d("TAG","---> $sourceList") //[People(name=李雷), People(name=周杰伦), People(name=汤姆), People(name=杰瑞)] Log.d("TAG","---> $copyList") // [People(name=李雷), People(name=周杰伦), People(name=汤姆)]
这些函数还可用于将集合转换为其他类型,例如根据 List 构建 Set,反之亦然。
val sourceList = mutableListOf(1, 2, 3) val copySet = sourceList.toMutableSet() copySet.add(3) copySet.add(4) println(copySet) // [1, 2, 3, 4]
集合的初始化可用于限制其可变性。例如,如果构建了一个MutableList的List引用,当你试图通过此引用修改集合的时候,编译器会抛出错误。
val sourceList = mutableListOf(1, 2, 3) val referenceList: List<Int> = sourceList //referenceList.add(4) // 编译错误 sourceList.add(4) println(referenceList) // [1, 2, 3, 4]
可以通过其他集合各种操作的结果来创建集合。例如,过滤列表会创建与过滤器匹配的新元素列表:
val numbers = listOf("one", "two", "three", "four") val longerThan3 = numbers.filter { it.length > 3 } println(longerThan3) // [three, four]
映射生成转换结果列表:
val numbers = setOf(1, 2, 3) println(numbers.map { it * 3 }) //[3, 6, 9] println(numbers.mapIndexed { idx, value -> value * idx }) // [0, 2, 6]
关联生成Map:
val numbers = listOf("one", "two", "three", "four") println(numbers.associateWith { it.length }) // {one=3, two=3, three=5, four=4}
有关 Kotlin 中集合操作的更多信息,可以看后文的集合操作部分。
Iterable<T>接口的继承者(包括Set与List)可以通过调用iterator()函数获得迭代器。
val numbers = listOf("one", "two", "three", "four") val numbersIterator = numbers.iterator() while (numbersIterator.hasNext()) { println(numbersIterator.next()) }
在集合中使用for循环时,将隐式获取迭代器。
普通for循环
val numbers = listOf("one", "two", "three", "four") for (item in numbers) { println(item) }
indices
val numbers = listOf("one", "two", "three", "four") for (index in numbers.indices){ //index取值为 0 <= index <= numbers.size - 1 相当于0..size - 1 println(numbers[index]) //"one", "two", "three", "four" }
使用 .. 区间
val numbers = listOf("one", "two", "three", "four") for (index in 0..2){ println(numbers[index]) // "one", "two", "three" }
downTo 倒叙
val numbers = listOf("one", "two", "three", "four") for (index in 3 downTo 1){ println(numbers[index]) // "four","three","two" }
步长 step
val numbers = listOf("one", "two", "three", "four") for (index in 0..3 step 2){ println(numbers[index]) // "one","three" }
使用 until 区间
val numbers = listOf("one", "two", "three", "four") for (index in 0 until 3){ // 包括 0 不包括 3 println(numbers[index]) // "one", "two", "three" }
withIndex
val numbers = listOf("one", "two", "three", "four") for ((index, name) in numbers.withIndex()){ println(numbers[index]) // "one", "two", "three", "four" println(name) // "one", "two", "three", "four" }
forEach
val numbers = listOf("one", "two", "three", "four") numbers.forEach { name -> println(name) } numbers.forEach { println(it) }
forEachIndexed
val numbers = listOf("one", "two", "three", "four") numbers.forEachIndexed { index, s -> println(numbers[index]) println(s) }
forEach循坏控制
forEach 是不支持 Break 和 Continue的,那么如何调出forEach循坏呢。
第二种:直接return,直接结束方法
直接return的方法会从最直接包围它的函数即 text 中返回,当forEach后面还有语句时,这些语句不会被执行。
fun text() { val listOf = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) listOf.forEach { println("$it") // 输出 1, 2, 3, 4, 5 if (5 <= it) return println("$it") // 输出 1 2 3 4 } println("text end") // 不执行 }
第二种:标签返回,只能跳出当前循坏,相当于 continue
fun text() { val listOf = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) listOf.forEach { println("$it") // 输出 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 if (5 <= it) return@forEach println("$it") // 输出 1, 2, 3, 4 } println("text end") // 输出 text end }
第三种:设置外置标签返回,结束forEach循坏
fun text() { val listOf = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) run outside@{ listOf.forEach { println("$it") // 输出 1, 2, 3, 4, 5 if (5 <= it) return@outside println("$it") // 输出 1, 2, 3, 4 } } println("text end") // 输出 text end }
val numbers = listOf("one", "two", "three", "four") var i = 0 while (i < numbers.size){ println(numbers[i]) i++ }
forEach
val mutableMap = mutableMapOf<String, String>().apply { this["1"] = "a";this["2"] = "b" } mutableMap.forEach{element -> element.key element.value }
for 循环
val mutableMap = mutableMapOf<String, String>().apply { this["1"] = "a";this["2"] = "b" } for ((key,value) in mutableMap){ println("$vulue") }
ListIterator 支持双向迭代,正向与反向。反向迭代通过 hasPrevious 与 previous 方法实现。只读list对应的是ListIterator。
可变list对应的是MutableListIterator。
val mutableListOf = mutableListOf(1, 2, 3, 4, 5) val listIterator = mutableListOf.listIterator() while (listIterator.hasNext()){ listIterator.nextIndex() // 下一个的索引 listIterator.next() // 下一个元素 } while (listIterator.hasPrevious()){ listIterator.previousIndex() // 上一个的索引 listIterator.previous() // 上一个元素 }
除了集合之外,Kotlin 标准库还包含另一种容器类型—序列(Sequence<T>)。 序列提供与Iterable相同的函数,但实现另一种方法来进行多步骤集合处理。
Iterable 与 Sequence 的区别:
因此,序列可避免生成中间步骤的结果,从而提高了整个集合处理链(映射、过滤、变换等)的性能。 但是,序列的延迟性质增加了一些开销,这些开销在处理较小的集合或进行更简单的计算时可能很重要。 因此,应该同时考虑使用 Sequence 与 Iterable,并确定在哪种情况更适合。
由元素
要创建一个序列,请调用sequenceOf()函数,列出元素作为其参数。
val numbersSequence = sequenceOf("four", "three", "two", "one")
由 Iterable
如果已经有一个Iterable对象(例如List或Set),则可以通过调用asSequence()从而创建一个序列。
val numbers = listOf("one", "two", "three", "four") val numbersSequence = numbers.asSequence()
由函数
创建序列的另一种方法是通过使用计算其元素的函数来构建序列。 要基于函数构建序列,请以该函数作为参数调用generateSequence()。 可以将第一个元素指定为显式值或函数调用的结果。 当提供的函数返回null时,序列生成停止。因此,以下示例中的序列是无限的。
val generateSequence = generateSequence(1) { it + 2 } println(generateSequence.take(5).toList()) // [1, 3, 5, 7, 9] println(oddNumbers.count()) // 错误:此序列是无限的。 var count = 3 val sequence = generateSequence { (count--).takeIf { it > 0 } // 当值变为非正数时,将返回null,序列会终止创建 } println(sequence.toList()) // [3, 2, 1]
由组块
最后,有一个函数可以逐个或按任意大小的组块生成序列元素: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()) // [1, 3, 5, 7, 9]
我们通过一个示例来看 Iterable 与 Sequence 之间的区别。
Iterable
示例如下:
fun text(){ val listOf = listOf("The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog") val lengthsList = listOf.filter { Log.d("TAG", "--- >filter -- $it ") it.length > 3 }.map { Log.d("TAG", "--- >map -- $it -- length == ${it.length}") it.length }.take(4) Log.d("TAG", "--- > Lengths of first 4 words longer than 3 chars ") Log.d("TAG", "--- > lengthsList== $lengthsList") }
输出结果:
--- >filter -- The --- >filter -- quick --- >filter -- brown --- >filter -- fox --- >filter -- jumps --- >filter -- over --- >filter -- the --- >filter -- lazy --- >filter -- dog --- >map -- quick -- length == 5 --- >map -- brown -- length == 5 --- >map -- jumps -- length == 5 --- >map -- over -- length == 4 --- >map -- lazy -- length == 4 --- > Lengths of first 4 words longer than 3 chars --- > lengthsList== [5, 5, 5, 4]
从结果可以看出:
列表处理如下图:
如流程图所示:Iterable 需要 23 步来完成整个链式处理。
Sequence
示例如下:
fun text(){ val listOf = listOf("The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog") val asSequence = listOf.asSequence() val sequence = asSequence.filter { Log.d("TAG", "--- >filter -- $it ") it.length > 3 }.map { Log.d("TAG", "--- >map -- $it -- length == ${it.length}") it.length }.take(4) Log.d("TAG", "--- > Lengths of first 4 words longer than 3 chars ") Log.d("TAG", "--- > lengthsList== ${sequence.toList()}") }
输出结果:
--- > Lengths of first 4 words longer than 3 chars --- >filter -- The --- >filter -- quick --- >map -- quick -- length == 5 --- >filter -- brown --- >map -- brown -- length == 5 --- >filter -- fox --- >filter -- jumps --- >map -- jumps -- length == 5 --- >filter -- over --- >map -- over -- length == 4 --- > lengthsList== [5, 5, 5, 4]
从输出结果可以看出:
序列处理流程图:
在此示例中,序列处理需要 18 个步骤,而不是 23 个步骤来执行列表操作。因此需要处理的数据量大的时候,序列的效率就会明显优于列表。
Kotlin 标准库为集合转换提供了一组扩展函数。 这些函数根据提供的转换规则从现有集合中构建新集合。 在此章节中,我们将概述可用的集合转换函数。
映射转换是从另一个集合的元素上的函数结果创建一个集合。
map()、mapIndexed()
基本的映射函数是map()。 它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。 结果的顺序与元素的原始顺序相同。 如果应用还要用到元素索引作为参数的转换,请使用mapIndexed()。
val numbers = listOf(1, 2, 3) println(numbers.map { it * 3 }) // [3, 6, 9] println(numbers.mapIndexed { idx, value -> value * idx }) // [0, 2, 6]
mapNotNull()、mapIndexedNotNull()
如果转换在某些元素上产生null值,则可以通过调用 mapNotNull() 函数取代 map() 或 mapIndexedNotNull() 取代mapIndexed()来从结果集中过滤掉null值。
val numbers = listOf(1, 2, 3) val mapNotNull = numbers.mapNotNull { if ( it == 2) null else it * 3 } println(mapNotNull) // [3, 9] val mapIndexedNotNull = numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx } println(mapIndexedNotNull) // [2, 6]
mapKeys()、mapValuse()
映射转换时,有两个选择:转换键,使值保持不变,反之亦然。 要将指定转换应用于键,请使用mapKeys()。反过来,mapValuse()转换值。 这两个函数都使用将映射条目作为参数的转换,因此可以操作其键与值。
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11) val mapKeys = numbersMap.mapKeys { it.key.toUpperCase() // 变大写 } Log.d("TAG","----> $mapKeys") // {KEY1=1, KEY2=2, KEY3=3, KEY11=11} val mapValues = numbersMap.mapValues { it.value + it.key.length // 值加上长度 } Log.d("TAG","----> $mapValues") // {key1=5, key2=6, key3=7, key11=16}
合拢转换是根据两个集合中具有相同位置的元素构建配对。
zip() 合拢
zip()返回Pair对象的列表(List)。 接收者集合的元素是这些配对中的第一个元素。 如果集合的大小不同,则zip()的结果为较小集合的大小,结果中不包含较大集合的后续元素。zip()也可以用中缀形式调用 a zip b。
val listOf = listOf(1, 2, 3) val listOf1 = listOf(4, 5, 6) val list = listOf zip listOf1 Log.d("TAG","--- > $list") // [(1, 4), (2, 5), (3, 6)]
也可以使用带有两个参数的转换函数来调用zip():接收者元素和参数元素。 在这种情况下,结果List包含在具有相同位置的接收者对和参数元素对上调用的转换函数的返回值。
val colors = listOf("red", "brown", "grey") val animals = listOf("fox", "bear", "wolf") println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"}) // 输出 结果 The Fox is red, The Bear is brown, The Wolf is grey]
unzip() 分割
要分割键值对列表,请调用 unzip()。当拥有 Pair 的 List 时,可以进行反向转换 unzipping 从这些键值对中构建两个列表:
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4) println(numberPairs.unzip()) // ([one, two, three, four], [1, 2, 3, 4])
关联转换允许从集合元素和与其关联的某些值构建 Map。 在不同的关联类型中,元素可以是关联 Map 中的键或值。
associateWith()
最基本的关联函数 associateWith(),创建一个 Map,其中原始集合的元素是键,并通过给定的转换函数从中产生值。 如果两个元素相等,则仅最后一个保留在 Map 中。
val numbers = listOf("one", "two", "three", "four","four") val associateWith = numbers.associateWith { it.length } Log.d("TAG", "---> associateWith == $associateWith") // {one=3, two=3, three=5, four=4}
associateBy()
associateBy() 默认需要传入一个函数,然后根据相应的函数的返回值创建键(key),集合元素作为值(value)来创建map。如果两个key相等,则仅最后一个保留在 Map 中。还可以同时传入key生成函数和value生成函数,然后生成对应的map。
val numbers = listOf("one", "two", "three", "four","four") val associateBy = numbers.associateBy { it.first().toUpperCase() } Log.d("TAG", "---> associateBy == $associateBy") // {O=one, T=three, F=four} val associateBy1 = numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }) Log.d("TAG", "---> associateBy1 == $associateBy1") // {O=3, T=5, F=4}
associate()
它需要一个 lambda 函数,该函数返回 Pair:键和相应 Map 条目的值。
请注意,associate()会生成临时的Pair对象,这可能会影响性能。 因此,当性能不是很关键或比其他选项更可取时,应使用associate()。
val numbers = listOf("one", "two", "three", "four","four") val associate = numbers.associate { it.first().toUpperCase() to it.last().toUpperCase() } Log.d("TAG", "---> associate == $associate") // {O=E, T=E, F=R}
如果你需要操作嵌套的集合,你会发现对嵌套集合元素进行打平访问的标准库函数很有用。
flatten()
该函数可以在一个集合的集合(例如,一个Set组成的List)上调用它。 该函数返回嵌套集合中的所有元素的一个List。
val listOf = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2, 5)) val flatten = listOf.flatten() Log.d("TAG", "--->flatten == $flatten") // [1, 2, 3, 4, 5, 6, 1, 2, 5]
flatMap()
它需要一个函数作为参数,这个参数函数将一个集合元素映射到另一个集合。 因此,flatMap()返回单个列表其中包含所有元素的值。 所以,flatMap()表现为map()(以集合作为映射结果)与flatten()的连续调用。
val mapOf = mapOf("122" to 2, "3455" to 4) val transform: (Map.Entry<String, Int>) -> List<Char> = { (key, value) -> key.take(value).toList() // 根据 值 的大小取 key的前几位 } val flatMap1 = mapOf.flatMap(transform) Log.d("TAG", "--->flatMap1 == $flatMap1") // [1, 2, 3, 4, 5, 5]
字符串表示是指将集合转换成字符串。kotlin提供了两个函数用于将集合转换成字符串:joinToString()与joinTo()。
joinToString() 根据提供的参数从集合元素构建单个String。joinTo() 执行相同的操作,但将结果附加到给定的Appendable(可追加)对象。
使用默认参数调用
当使用默认参数调用时,函数返回的结果类似于在集合上调用toString():各元素的字符串表示形式以空格分隔而成的String。
val numbers = listOf("one", "two", "three", "four") val joinToString = numbers.joinToString() // 返回一个String Log.d("TAG", "---> $numbers") // [one, two, three, four] Log.d("TAG", "---> $joinToString") // // one, two, three, four val listString = StringBuffer("The list of numbers: ") numbers.joinTo(listString) Log.d("TAG", "---> $listString") // The list of numbers: one, two, three, four
构建自定义字符串表示形式
要构建自定义字符串表示形式,可以在函数参数separator、prefix与postfix中指定其参数。 结果字符串将以 prefix 开头,以postfix 结尾。除最后一个元素外,separator 将位于每个元素之后。
val numbers1 = listOf("one", "two", "three", "four") val joinToString1 = numbers1.joinToString(separator = " | ", prefix = "start: ", postfix = ": end") Log.d("TAG", "---> $joinToString1") // start: one | two | three | four: end
较大集合指定范围
对于较大的集合,可能需要指定 limit(范围)。将包含在结果中元素的数量。 如果集合大小超出 limit ,所有其他元素将被truncated 参数的单个值替换。
val numbers = (1..100).toList() val joinToString = numbers.joinToString(limit = 10, truncated = "<...>") Log.d("TAG", "---> $joinToString") // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, <...>
自定义元素
要自定义元素本身的表示形式,请提供 transform 函数。
val numbers = listOf("one", "two", "three", "four") val joinToString = numbers.joinToString { "Element: ${it.toUpperCase()}" } Log.d("TAG", "---> $joinToString") // Element: ONE, Element: TWO, Element: THREE, Element: FOUR
在Kotlin中,过滤条件由谓词定义,指接受一个集合元素并且返回布尔值的 lambda 表达式:true说明给定元素与谓词匹配,false则表示不匹配。
filter()
基本过滤函数是filter(),返回与其匹配的集合元素。对于List和Set,过滤结果都是一个List,对Map来说结果还是一个Map。
val numbers = listOf("one", "two", "three", "four") val longerThan3 = numbers.filter { it.length > 3 } Log.d("TAG", "----> $longerThan3") // [three, four] 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} Log.d("TAG", "----> $filteredMap") // {key11=11}
filterIndexed()
filter() 中的谓词只能检查元素的值。如果想在过滤中使用元素在集合中的位置,应该使用 filterIndexed()。它接受一个带有两个参数的谓词:元素的索引和元素的值。
val numbers = listOf("one", "two", "three", "four") val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) } Log.d("TAG", "----> $filteredMap") //[two, four]
filterNot()
如果想使用否定条件来过滤集合,请使用 filterNot()。它返回一个让谓词产生 false 的元素列表。
val numbers = listOf("one", "two", "three", "four") val filteredNot = numbers.filterNot { it.length <= 3 } Log.d("TAG", "----> $filteredNot") // [three, four]
filterIsInstance()
filterIsInstance() 返回给定类型的集合元素。在一个 List<Any> 上被调用时,filterIsInstance<T>() 返回一个 List<T>,从而让你能够在集合元素上调用 T 类型的函数。
val numbers = listOf(null, 1, "two", 3.0, "four") val filterIsInstance = numbers.filterIsInstance<String>() Log.d("TAG","----> $filterIsInstance") // [two, four]
filterNotNull()
filterNotNull() 返回所有的非空元素。在一个 List<T?> 上被调用时,filterNotNull() 返回一个 List<T: Any>,从而让你能够将所有元素视为非空对象。
val numbers = listOf(null, 1, "two", 3.0, "four") val filterIsInstance = numbers.filterNotNull() Log.d("TAG","----> $filterIsInstance") // [1, two, 3.0, four]
partition()
partition() 通过一个谓词过滤集合并且将不匹配的元素存放在一个单独的列表中。因此,你将得到一个 List 的 Pair 作为返回值:第一个列表包含与谓词匹配的元素并且第二个列表包含原始集合中的所有其他元素。
val numbers = listOf("one", "two", "three", "four") val (match, rest) = numbers.partition { it.length > 3 } Log.d("TAG","----> $match") // [three, four] Log.d("TAG","----> $rest") // [one, two]
any():如果至少有一个元素匹配给定谓词,那么 any() 返回 true。
none() :如果没有元素与给定谓词匹配,那么 none() 返回 true。
all() :如果所有元素都匹配给定谓词,那么 all() 返回 true。
注意,在一个空集合上使用任何有效的谓词去调用 all() 都会返回 true 。这种行为在逻辑上被称为 vacuous truth。
val numbers = listOf("one", "two", "three", "four") val any = numbers.any { it.endsWith("e") } // true val none = numbers.none { it.endsWith("a") } // true val all = numbers.all { it.endsWith("e") } // false val all1 = emptyList<Int>().all { it > 5 } // true
any() 和 none() 也可以不带谓词使用:在这种情况下它们只是用来检查集合是否为空。 如果集合中有元素,any() 返回 true,否则返回 false;none() 则相反。
val numbers = listOf("one", "two", "three", "four") val empty = emptyList<String>() val any = numbers.any() // true val any1 = empty.any() // false val none = numbers.none() // false val none1 = empty.none() // true
在 Kotlin 中,为集合定义了 plus (+) 和 minus (-) 操作符。 它们把一个集合作为第一个操作数;第二个操作数可以是一个元素或者是另一个集合。 返回值是一个新的只读集合:
val numbers = listOf("one", "three","two", "three","four") val plusList = numbers + "five" val minusList = numbers - listOf("three", "four") val minusList1 = numbers - "three" Log.d("TAG","---> $plusList") // [one, three, two, three, four, five] Log.d("TAG","---> $minusList") // [one, two] Log.d("TAG","---> $minusList1") // [one, two, three, four]
由于需要访问元素的键,plus(+)与 minus(-)运算符对 map 的作用与其他集合不同。
plus 返回 包含两个操作数元素(左侧的 Map 与右侧的 Pair 或另一个 Map) 的 Map 。 当右侧操作数中有左侧 Map 中已存在的键时,该条目将使用右侧的值。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) val plus = numbersMap + Pair("four", 4) val plus1 = numbersMap.plus(Pair("one", 10)) val plus2 = numbersMap + mapOf("five" to 5, "one" to 11) Log.d("TAG","----> $plus") // {one=1, two=2, three=3, four=4} Log.d("TAG","----> $plus1") // {one=10, two=2, three=3} Log.d("TAG","----> $plus2") // {one=11, two=2, three=3, five=5}
minus 将根据左侧 Map 条目创建一个新 Map ,然后剔除 右侧操作数 中包含的键的条目。 因此,右侧操作数可以是单个键或键的集合: list 、 set 等,不能是map,如果传入map,则将map看成是单个key,会导致操作不成功。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) val map = numbersMap - "one" val map1 = numbersMap - listOf("two", "four") val map2 = numbersMap - mapOf("one" to 1) Log.d("TAG","----> $map") // {two=2, three=3} Log.d("TAG","----> $map1") // {one=1, three=3} Log.d("TAG","----> $map2") // {one=1, two=2, three=3}
Kotlin 标准库提供用于对集合元素进行分组的扩展函数。
groupBy()
基本函数 groupBy() 使用一个 lambda 函数并返回一个 Map。 在此 Map 中,每个键都是 lambda 结果,而对应的值是返回此结果的元素 List。 例如,可以使用此函数将 String 列表按首字母分组。
val numbers = listOf("one", "two", "three", "four", "five") val groupBy = numbers.groupBy { it.first().toUpperCase() } Log.d("TAG","----> $groupBy") // {O=[one], T=[two, three], F=[four, five]}
在带有两个 lambda 参数 的 groupBy() 结果 Map 中,由 keySelector 函数生成 键映,由 valueTransform函数生成值。
val numbers = listOf("one", "two", "three", "four", "five") val groupBy1 = numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.toUpperCase() }) Log.d("TAG","----> $groupBy1") // {o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}
groupingBy()
如果要对元素进行分组,然后一次将操作应用于所有分组,请使用 groupingBy() 函数。 它返回一个 Grouping 类型的实例。 通过 Grouping 实例,可以以一种惰性的方式将操作应用于所有组:这些分组实际上是刚好在执行操作前构建的。
Grouping 支持以下操作:
val numbers = listOf("one", "two", "three", "four", "five", "six") val eachCount = numbers.groupingBy { it.first() }.eachCount() Log.d("TAG","----> $eachCount") // {o=1, t=2, f=2, s=1} val fold = listOf(1, 2, 3).fold(10, { acc, i -> acc + i }) Log.d("TAG","----> $fold") // 计算过程 10 + 1 + 2 + 3 输出 16 val reduce = listOf(1, 2, 3).reduce { acc, i -> acc * i } Log.d("TAG","----> $reduce") // 计算过程 1*2*3 输出 6 val numbers1 = listOf(3, 4, 5, 6, 7, 8, 9) val aggregated = numbers1.groupingBy { it % 3 }.aggregate { key, accumulator: StringBuilder?, element, first -> if (first) // first element StringBuilder().append(key).append(":").append(element) else accumulator!!.append("-").append(element) } Log.d("TAG","----> $aggregated") // {0=0:3-6-9, 1=1:4-7, 2=2:5-8}
Kotlin 标准库包含用于取集合的一部分的扩展函数。 这些函数提供了多种方法来选择结果集合的元素:显式列出其位置、指定结果大小等。
slice() 返回具有给定索引的集合元素列表。 索引既可以是作为区间传入的也可以是作为整数值的集合传入的。返回列表的顺序由输入的索引顺序决定。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.slice(1..3)) // [two, three, four] println(numbers.slice(0..4 step 2)) // [one, three, five] println(numbers.slice(setOf(3, 5, 0))) // [four, six, one]
take():从头开始获取指定数量的元素。 takeLast():从尾开始获取指定数量的元素。 当调用的数字大于集合的大小时,两个函数都将返回整个集合。
要从头或从尾去除给定数量的元素,请调用 drop() 或 dropLast() 函数。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.take(3)) // [one, two, three] println(numbers.takeLast(3)) // [four, five, six] println(numbers.drop(1)) // [two, three, four, five, six] println(numbers.dropLast(5)) // [one]
还可以使用谓词来定义要获取或去除的元素的数量。 有四个与上述功能相似的函数:
takeWhile()
takeWhile() 是带有谓词(判断条件)的 take():它将不停获取集合元素,直到遇到不符合条件的首个元素为止,然后将符合条件的元素作为一个新集合返回,不包括首个不符合条件的元素。如果首个集合元素与谓词匹配,则结果为空。
takeLastWhile()
takeLastWhile() 与 takeLast() 类似:它从集合末尾获取与谓词匹配的元素区间。区间的首个元素是与谓词不匹配的最后一个元素右边的元素。如果最后一个集合元素与谓词匹配,则结果为空。
dropWhile()
dropWhile() 与具有相同谓词的 takeWhile() 相反:它将首个与谓词不匹配的元素返回到末尾。
dropLastWhile()
dropLastWhile() 与具有相同谓词的 takeLastWhile() 相反:它返回从开头到最后一个与谓词不匹配的元素。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.takeWhile { !it.startsWith('f') }) // [one, two, three] println(numbers.takeWhile { it.startsWith('f') }) // [] 返回空 println(numbers.takeLastWhile { it != "three" }) // [four, five, six] println(numbers.dropWhile { it.length == 3 }) // [three, four, five, six] println(numbers.dropLastWhile { it.contains('i') }) // [one, two, three, four]
chunked() :将集合分解为给定大小的“块”。
fun <T> Iterable<T>.chunked(size: Int): List<List<T>>
chunked() 采用一个参数(块的大小),并返回一个 List:其中包含给定大小的 List。
val numbers = (0..13).toList() println(numbers.chunked(3)) // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13]]
还可以立即对返回的块应用转换。 为此,请在调用 chunked() 时将转换作为 lambda 函数提供。
fun <T, R> Iterable<T>.chunked(size: Int, transform: (List<T>) -> R): List<R>
lambda 参数是集合的一块。当通过转换调用 chunked() 时, 这些块是临时的 List,应立即在该 lambda 中使用。
val numbers = (0..13).toList() println(numbers.chunked(3) { it.sum() }) //`it` 为原始集合的一个块,输出 [3, 12, 21, 30, 25]
windowed():可以检索给定大小的集合元素中所有可能区间。
fun <T> Iterable<T>.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List<List<T>>
它返回一个元素区间列表。 与 chunked() 不同,windowed() 返回从每个集合元素开始的元素区间(窗口)。 所有窗口都作为单个 List 的元素返回。
val numbers = listOf("one", "two", "three", "four", "five") val windowed = numbers.windowed(3) Log.d("TAG","----> $windowed") //输出结果 [[one, two, three], [two, three, four], [three, four, five]]
windowed() 通过可选参数提供更大的灵活性:
fun <T, R> Iterable<T>.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List<T>) -> R): List<R>
step:定义两个相邻窗口的第一个元素之间的距离。默认情况下,该值为 1,因此结果包含从所有元素开始的窗口。如果将 step 增加到 2,将只收到以奇数元素开头的窗口:第一个、第三个等。
partialWindows:包含从集合末尾的元素开始的较小的窗口。例如,如果请求三个元素的窗口,就不能为最后两个元素构建它们。在本例中,启用 partialWindows 将包括两个大小为2与1的列表。
lambda:可以立即对返回的区间应用转换。 为此,在调用 windowed() 时将转换作为 lambda 函数提供。
val numbers = (1..10).toList() val windowed = numbers.windowed(3, step = 2, partialWindows = true) val windowed1 = numbers.windowed(3, step = 2, partialWindows = false) val windowed2 = numbers.windowed(3) { it.sum() } Log.d("TAG","----> $windowed") // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]] Log.d("TAG","----> $windowed1") // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]] Log.d("TAG","----> $windowed2") // [6, 9, 12, 15, 18, 21, 24, 27]
要构建两个元素的窗口,有一个单独的函数——zipWithNext()。
fun <T> Iterable<T>.zipWithNext(): List<Pair<T, T>> fun <T, R> Iterable<T>.zipWithNext(transform: (a: T, b: T) -> R): List<R>
它创建接收器集合的相邻元素对。 请注意,zipWithNext() 不会将集合分成几对;它为 每个 元素创建除最后一个元素外的对,因此它在 [1, 2, 3, 4] 上的结果为 [[1, 2], [2, 3], [3, 4]],而不是 [[1, 2],[3, 4]]。 zipWithNext() 也可以通过转换函数来调用;它应该以接收者集合的两个元素作为参数。
val numbers = listOf("one", "two", "three", "four", "five") val zipWithNext = numbers.zipWithNext() val zipWithNext1 = numbers.zipWithNext { s1, s2 -> s1.length > s2.length } Log.d("TAG","----> $zipWithNext") // [(one, two), (two, three), (three, four), (four, five)] Log.d("TAG","----> $zipWithNext1") // [false, false, true, false]
Kotlin 集合提供了一套从集合中检索单个元素的函数。 此项描述的函数适用于 list 和 set。
elementAt():检索特定位置的元素
用一个整数作为参数来调用它,你会得到给定位置的集合元素。 第一个元素的位置是 0,最后一个元素的位置是 (size - 1)。
elementAt() 对于不提供索引访问或非静态已知提供索引访问的集合很有用。 在使用 List 的情况下,使用索引访问操作符 (get() 或 [])更为习惯。
val numbers = linkedSetOf("one", "two", "three", "four", "five") // LinkedHashSet val elementAt = numbers.elementAt(3) Log.d("TAG","----> $elementAt") // four val numbersSortedSet = sortedSetOf("one", "two", "three", "four") // TreeSet val elementAt1 = numbersSortedSet.elementAt(0) //升序排列 Log.d("TAG","----> $elementAt1") // four
first() 和 last():检索集合的第一个和最后一个元素。
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.first()) // one println(numbers.last()) // five
elementAt() 的安全变体:为了避免在检索位置不存在的元素时出现异常
elementAtOrNull():当指定位置超出集合范围时,elementAtOrNull() 返回 null。
elementAtOrElse():接受一个 lambda 表达式,该表达式能将一个 Int 参数映射为一个集合元素类型的实例。 当使用一个越界位置来调用时,elementAtOrElse() 返回对给定值调用该 lambda 表达式的结果。
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.elementAtOrNull(5)) // null println(numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"}) // 输出 The value for index 5 is undefined
first() 和 last()
函数 first() 和 last() 还可以让你在集合中搜索与给定谓词匹配的元素。 当你使用测试集合元素的谓词调用 first() 时,你会得到对其调用谓词产生 true 的第一个元素。 反过来,带有一个谓词的 last() 返回与其匹配的最后一个元素。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.first { it.length > 3 }) // three println(numbers.last { it.startsWith("f") }) // five
firstOrNull() 和 lastOrNull()
如果没有元素与谓词匹配,first() 和 last()都会抛异常。 为了避免它们,请改用 firstOrNull() 和 lastOrNull():如果找不到匹配的元素,它们将返回 null。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.firstOrNull { it.length > 6 }) // null
find()和findLast()
如果别名更适合你的情况,那么可以使用别名:使用 find() 代替 firstOrNull(),使用 findLast() 代替 lastOrNull()
val numbers = listOf(1, 2, 3, 4) println(numbers.find { it % 2 == 0 }) // 2 println(numbers.findLast { it % 2 == 0 }) // 4
如果需要检索集合的一个随机元素,那么请调用 random() 函数。 你可以不带参数或者使用一个 Random 对象作为随机源来调用它。
val numbers = listOf(1, 2, 3, 4) val random1 = numbers.random() val random = numbers.random(Random(1)) // 在 0 到 1的范围内取值,不包括1 Log.d("TAG","---> $random1") // 3 Log.d("TAG","---> $random") // 1
如需检查集合中某个元素的存在,可以使用 contains() 函数。 如果存在一个集合元素等于(equals())函数参数,那么它返回 true。 你可以使用 in 关键字以操作符的形式调用 contains()。
如需一次检查多个实例的存在,可以使用这些实例的集合作为参数调用 containsAll()。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.contains("four")) // true println("zero" in numbers) // false println(numbers.containsAll(listOf("four", "two"))) // true println(numbers.containsAll(listOf("one", "zero"))) // false
此外,你可以通过调用 isEmpty() 和 isNotEmpty() 来检查集合中是否包含任何元素。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.isEmpty()) // false println(numbers.isNotEmpty()) // true val empty = emptyList<String>() println(empty.isEmpty()) // true println(empty.isNotEmpty()) // false
元素的顺序是某些集合类型的一个重要方面。 例如,如果拥有相同元素的两个列表的元素顺序不同,那么这两个列表也不相等。
sorted() :使集合元素按照其自然顺序升序排序
sortedDescending():使集合元素按照其自然顺序降序排序。
这些函数适用于 Comparable 元素的集合。
val numbers = listOf("one", "two", "three", "four") println("${numbers.sorted()}") // 升序 [four, one, three, two] println("${numbers.sortedDescending()}") // 降序 [two, three, one, four]
sortedBy() 和 sortedByDescending()
为了按照自定义顺序排序或者对不可比较对象排序,可以使用函数 sortedBy() 和 sortedByDescending()。 它们接受一个将集合元素映射为 Comparable 值的选择器函数,并以该值的自然顺序对集合排序。
val numbers = listOf("one", "two", "three", "four") val sortedNumbers = numbers.sortedBy { it.length } // 根据长度升序 println("$sortedNumbers") // [one, two, four, three] val sortedByLast = numbers.sortedByDescending { it.last() } // 根据最后一个字符倒叙 println("$sortedByLast") // [four, two, one, three]
sortedWith()
如需为集合排序定义自定义顺序,可以提供自己的 Comparator。 为此,调用传入 Comparator 的 sortedWith() 函数。 使用此函数,按照字符串长度排序如下所示:
val numbers = listOf("one", "two", "three", "four") println("${numbers.sortedWith(compareBy { it.length })}") // [one, two, four, three]
reversed()
你可以使用 reversed() 函数以相反的顺序检索集合。reversed() 返回带有元素副本的新集合。 因此,如果你之后改变了原始集合,这并不会影响先前获得的 reversed() 的结果。
val numbers = mutableListOf("one", "two", "three", "four") val reversed = numbers.reversed() Log.d("TAG","---> $reversed") // [four, three, two, one] numbers.removeAt(0) //对原始集合进行修改 numbers[1] = "five" Log.d("TAG","---> $numbers") // [two, five, four] Log.d("TAG","---> $reversed") //[four, three, two, one] 对原始集合额修改不影响reversed结果
当集合元素是自定义类型时,对原始集合进行的添加删除等操作不会影响先前获取到的reversed() 的结果,但是对集合元素属性的相关操作会影响先前获取到的reversed() 的结果。
data class People(var name:String) val peoples = mutableListOf(People("李林"), People("李雷"), People("汤姆"), People("翠花")) val reversed = peoples.reversed() Log.d("TAG","---> $reversed") // [People(name=翠花), People(name=汤姆), People(name=李雷), People(name=李林)] peoples.removeAt(0) // 原始集合的移除操作不会影响先前reversed获取到的结果 peoples[1].name = "周杰伦"// 原始集合元素属性额修改会影响先前reversed获取到的结果 Log.d("TAG","---> $peoples") // [People(name=李雷), People(name=周杰伦), People(name=翠花)] Log.d("TAG","---> $reversed") // [People(name=翠花), People(name=周杰伦), People(name=李雷), People(name=李林)]
asReversed()
asReversed()返回相同集合实例的一个反向视图,因此,如果原始列表不会发生变化,那么它会比 reversed() 更轻量,更合适。
如果原始列表是可变的,那么其所有更改都会反映在其反向视图中,反之亦然。
val numbers = mutableListOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers) // [four, three, two, one] numbers.add("five") println(reversedNumbers) // [five, four, three, two, one]
但是,如果列表的可变性未知或者源根本不是一个列表,那么reversed()更合适,因为其结果是一个未来不会更改的副本。
最后,shuffled() 函数返回一个包含了以随机顺序排序的集合元素的新的 List。 你可以不带参数或者使用 Random 对象来调用它。
val numbers = listOf("one", "two", "three", "four") println(numbers.shuffled()) // [four, three, one, two]
Kotlin 集合包含用于常用的 聚合操作 (基于集合内容返回单个值的操作)的函数 。 其中大多数是众所周知的,并且其工作方式与在其他语言中相同。
min() 与 max():分别返回最小和最大的元素。
average():返回数字集合中元素的平均值。
sum():返回数字集合中元素的总和。
count():返回集合中元素的数量。
val numbers = listOf(6, 42, 10, 4) println("Count: ${numbers.count()}") // Count: 4 println("Max: ${numbers.max()}") //Max: 42 println("Min: ${numbers.min()}") // Min: 4 println("Average: ${numbers.average()}") // Average: 15.5 println("Sum: ${numbers.sum()}") // Sum: 62
还有一些通过某些选择器函数或自定义 Comparator 来检索最小和最大元素的函数。
maxBy()与minBy():接受一个选择器函数并返回使选择器返回最大或最小值的元素。
maxWith()与minWith():接受一个 Comparator 对象并且根据此 Comparator 对象返回最大或最小元素。
val numbers = listOf(5, 42, 10, 4) val min3Remainder = numbers.minBy { it % 3 } println(min3Remainder) //输出结果:42 val strings = listOf("one", "two", "three", "four") val longestString = strings.maxWith(compareBy { it.length }) println(longestString) //输出结果:three
此外,还有一些高级的求和函数,它们接受一个函数并返回对所有元素调用此函数的返回值的总和。
sumBy():使用对集合元素调用返回 Int 值的函数。
sumByDouble():与返回 Double 的函数一起使用。
val numbers = listOf(5, 42, 10, 4) println(numbers.sumBy { it * 2 }) // 122 println(numbers.sumByDouble { it.toDouble() / 2 }) // 30.5
函数 reduce()与fold() 都是依次将所提供的操作函数应用于集合元素并返回累积的结果。 操作有两个参数:先前的累积值和集合元素。
这两个函数的区别在于:fold() 接受一个初始值并将其用作第一步的累积值,而 reduce() 的第一步则将第一个和第二个元素作为第一步的操作参数。
val numbers = listOf(5, 2, 10, 4) val sum = numbers.reduce { sum, element -> sum + element * 2 } //计算过程 第一步将 5、2 作为参数,即sum =5,element =2,因此5不会被乘以2,即5 + 2*2 + 10*2 + 4*2 Log.d("TAG","---> $sum") // 输出 37 //计算过程 第一步将 0、5 作为参数,即sum =0,element =5,因此5会被乘以2,即0 + 5*2 + 2*2 + 10*2 + 4*2 val sumDoubled = numbers.fold(10) { sum, element -> sum + element * 2 } Log.d("TAG","---> $sumDoubled") // 输出 42
如需将函数以相反的顺序应用于元素,可以使用函数 reduceRight() 和 foldRight()。它们的工作方式类似于 fold() 和 reduce(),但从最后一个元素开始,然后再继续到前一个元素。 记住,在使用 foldRight 或 reduceRight 时,操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
val numbers = listOf(5, 2, 10, 4) val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 } Log.d("TAG","---> $sumDoubledRight") // 输出 42
使用函数 reduceIndexed() 和 foldIndexed() 传递元素索引作为操作的第一个参数。最后,还有将这些操作从右到左应用于集合元素的函数:reduceRightIndexed() 与 foldRightIndexed()。
val numbers = listOf(5, 2, 10, 4) val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum } Log.d("TAG","---> $sumEven") // 15 val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum } Log.d("TAG","---> $sumEvenRight") // 15
12.5、其他
所有reduce操作都会在空集合上引发异常。如果想要在空集合时返回null,请使用它们的*OrNull()对应项:
reduceOrNull()、reduceRightOrNull()、reduceIndexedOrNull()、reduceRightIndexedOrNull()
这些函数用法与上面一样,就不一一举例子了。
可变集合支持更改集合内容的操作,例如添加或删除元素。 在此章节,我们将描述实现 MutableCollection 的所有写操作。 有关 List、Set、Map 可用的更多特定操作,请分别参见 List 相关操作、Set 相关操作、Map 相关操作。
add()
要将单个元素添加到列表或集合,请使用 add() 函数。指定的对象将添加到集合的末尾。
val numbers = mutableListOf(1, 2, 3, 4) numbers.add(5) println(numbers) // [1, 2, 3, 4, 5]
addAll()
addAll() 将参数对象的每个元素添加到列表或集合中。参数可以是 Iterable、Sequence 或 Array。 接收者的类型和参数可能不同,例如,你可以将所有内容从 Set 添加到 List。
当在列表上调用时,addAll() 会按照在参数中出现的顺序添加各个新元素。 你也可以调用 addAll() 时指定一个元素位置作为第一参数。 参数集合的第一个元素会被插入到这个位置。 其他元素将跟随在它后面,将接收者元素移到末尾。
val numbers = mutableListOf(1, 2, 5, 6) numbers.addAll(arrayOf(7, 8)) println(numbers) // [1, 2, 5, 6, 7, 8] numbers.addAll(2, setOf(3, 4)) // 指定开始添加的位置 println(numbers) // [1, 2, 3, 4, 5, 6, 7, 8]
plus(+)、plusAssign (+=) 运算符
你还可以使用 plus 运算符与plusAssign (+=) 添加元素。 当应用于可变集合时,+= 将第二个操作数(一个元素或另一个集合)追加到集合的末尾。
val numbers = mutableListOf("one", "two") numbers += "three" println(numbers) // [one, two, three] numbers += listOf("four", "five") println(numbers) // [one, two, three, four, five]
remove()
若要从可变集合中移除元素,请使用 remove() 函数。 remove() 接受元素值,并删除该值的一个匹配项。
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.remove(3) println(numbers) // [1, 2, 4, 3] 删除了第一个 `3` numbers.remove(5) println(numbers) // [1, 2, 4, 3] 什么都没删除
removeAll() 移除全部、retainAll() 保留全部、clear() 清空
removeAll():移除参数集合中存在的所有元素。 或者,你可以用谓词作为参数来调用它;在这种情况下,函数移除谓词产生 true 的所有元素。
retainAll() 与 removeAll() 相反:它移除除参数集合中的元素之外的所有元素。 当与谓词一起使用时,它只留下与之匹配的元素。
clear():从列表中移除所有元素并将其置空。
val numbers = mutableListOf(1, 2, 3, 4) println(numbers) // [1, 2, 3, 4] numbers.retainAll { it >= 3 } //保留大于等于3的 println(numbers) // 输出 [3, 4] numbers.clear() // 清空 println(numbers) // 输出 [] val numbersSet = mutableSetOf("one", "two", "three", "four") numbersSet.removeAll(setOf("one", "two")) // 移除 "one", "two" println(numbersSet) // 输出 [three, four]
minus(-)、minusAssign (-=)
这两个函数参数可以是元素类型的单个实例或另一个集合。 右边是单个元素时,会移除它的第一个匹配项。 如果它是一个集合,那么它的所有元素的每次出现都会删除。 例如,如果列表包含重复的元素,它们将被同时删除。 第二个操作数可以包含集合中不存在的元素。这些元素不会影响操作的执行。
val numbers = mutableListOf("one", "two", "three", "three", "four") numbers -= "three" // 移除第一个 "three" println(numbers) // [one, two, three, four] numbers -= listOf("four", "five") //numbers -= listOf("four") // 与上述相同 println(numbers) // [one, two, three]
list 和 map 还提供更新元素的操作。 它们在 List 相关操作与 Map 相关操作中有所描述。 对于 set 来说,更新没有意义,因为它实际上是移除一个元素并添加另一个元素。
List 是 Kotlin 标准库中最受欢迎的集合类型。对列表元素的索引访问为 List 提供了一组强大的操作。
List 支持按索引取元素的常用操作有: elementAt() 、 first() 、 last() 、取单个元素(第10节)中列出的其他操作。 List 的特点是能通过索引访问特定元素,因此读取元素的最简单方法是按索引检索它。 这是通过 get() 函数或简写语法 [index] 来传递索引参数完成的。
如果 List 长度小于指定的索引,则抛出异常。 另外,还有两个函数能避免此类异常:
getOrElse():提供用于计算默认值的函数,如果集合中不存在索引,则返回默认值。
getOrNull():返回 null 作为默认值。
val numbers = listOf(1, 2, 3, 4) println(numbers.get(0)) // 1 println(numbers[0]) // 1 //numbers.get(5) // exception! println(numbers.getOrNull(5)) // null println(numbers.getOrElse(5, {it})) // 5
除了 取集合的一部分(第9节)中常用的操作, List 还提供 subList() 该函数将指定元素范围的视图作为列表返回。 因此,如果原始集合的元素发生变化,则它在先前创建的子列表中也会发生变化,反之亦然。
val numbers = (0..13).toList() println(numbers.subList(3, 6)) // [3, 4, 5]
indexOf()、lastIndexOf()
在任何列表中,都可以使用 indexOf() 或 lastIndexOf() 函数找到元素的位置。 它们返回与列表中给定参数相等的元素的第一个或最后一个位置。 如果没有这样的元素,则两个函数均返回 -1。
val numbers = listOf(1, 2, 3, 4, 2, 5) println(numbers.indexOf(2)) // 1 println(numbers.lastIndexOf(2)) // 4
indexOfFirst()、indexOfLast()
还有一对函数接受谓词并搜索与之匹配的元素:
indexOfFirst() 返回与谓词匹配的第一个元素的索引,如果没有此类元素,则返回 -1。
indexOfLast() 返回与谓词匹配的最后一个元素的索引,如果没有此类元素,则返回 -1。
val numbers = mutableListOf(1, 2, 3, 4) println(numbers.indexOfFirst { it > 2}) println(numbers.indexOfLast { it % 2 == 1})
二分查找算法 binarySearch()
它的工作速度明显快于其他内置搜索功能,但要求该列表按照一定的顺序(自然排序或函数参数中提供的另一种排序)按升序排序过。 否则,结果是不确定的。
要搜索已排序列表中的元素,请调用 binarySearch() 函数,并将该值作为参数传递。 如果存在这样的元素,则函数返回其索引;否则,将返回 (-insertionPoint - 1),其中 insertionPoint 为应插入此元素的索引,以便列表保持排序。 如果有多个具有给定值的元素,搜索则可以返回其任何索引。
还可以指定要搜索的索引区间:在这种情况下,该函数仅在两个提供的索引之间搜索。
val numbers = mutableListOf("one", "two", "three", "four") numbers.sort() Log.d("TAG","----> $numbers") // [four, one, three, two] Log.d("TAG","----> ${numbers.binarySearch("two")}") // 3 Log.d("TAG","----> ${numbers.binarySearch("z")}") // -5 Log.d("TAG","----> ${numbers.binarySearch("two", 0, 2)}") // -3
Comparator 二分搜索
如果列表元素不是 Comparable,则应提供一个用于二分搜索的 Comparator。 该列表必须根据此 Comparator 以升序排序。来看一个例子:
val productList = listOf( Product("WebStorm", 49),Product("AppCode", 99),Product("AppCode", 129), Product("DotTrace", 129),Product("ReSharper", 149)) val binarySearch = productList.binarySearch(Product("AppCode", 129), compareBy<Product> { it.price }.thenBy { it.name }) Log.d("TAG","--> $binarySearch") // 2
当列表使用与自然排序不同的顺序时(例如,对 String 元素不区分大小写的顺序),自定义 Comparator 也很方便。
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) }) // 1 }
Comparator 与比较函数二分搜索都可以针对列表区间执行。
除了 集合写操作(第13接)中描述的集合修改操作之外,可变列表还支持特定的写操作。 这些操作使用索引来访问元素以扩展列表修改功能。
要将元素添加到列表中的特定位置,请使用 add() 或 addAll() 并提供元素插入的位置作为附加参数。 位置之后的所有元素都将向右移动。
val numbers = mutableListOf("one", "five", "six") numbers.add(1, "two") numbers.addAll(2, listOf("three", "four")) println(numbers) // [one, two, three, four, five, six]
set() 及其操作符形式 []
set() 不会更改其他元素的索引。
val numbers = mutableListOf("one", "five", "three") numbers[1] = "two" println(numbers) // [one, two, three]
fill()
fill() 简单地将所有集合元素的值替换为指定值。
val numbers = mutableListOf(1, 2, 3, 4) numbers.fill(3) println(numbers) // [3, 3, 3, 3]
removeAt()
要从列表中删除指定位置的元素,请使用 removeAt() 函数,并将位置作为参数。 在元素被删除之后出现的所有元素索引将减 1。
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.removeAt(1) println(numbers) // [1, 3, 4, 3]
removeFirst()和removeLast()、removeFirstOrNull()和removeLastOrNull()
要删除第一个和最后一个元素,有方便的快捷方式removeFirst()和removeLast()。注意,在空列表上,它们抛出一个异常。要接收null,请使用removeFirstOrNull()和removeLastOrNull()
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.removeFirst() numbers.removeLast() println(numbers) // [2, 3, 4] val empty = mutableListOf<Int>() // empty.removeFirst() // NoSuchElementException: List is empty. empty.removeFirstOrNull() //null
在 集合排序(第11节)中,描述了按特定顺序检索集合元素的操作。 对于可变列表,标准库中提供了类似的扩展函数,这些扩展函数可以执行相同的排序操作。 将此类操作应用于列表实例时,它将更改指定实例中元素的顺序。
就地排序函数的名称与应用于只读列表的函数的名称相似,但没有 ed/d 后缀:
sort* 在所有排序函数的名称中代替 sorted*:sort()、sortDescending()、sortBy() 等等。
随机排序 shuffle() 代替 shuffled()。
倒叙 reverse() 代替 reversed()。
asReversed() 在可变列表上调用会返回另一个可变列表,该列表是原始列表的反向视图。在该视图中的更改将反映在原始列表中。 以下示例展示了可变列表的排序函数:
val numbers = mutableListOf("one", "two", "three", "four") numbers.sort() println("Sort into ascending: $numbers") // [four, one, three, two] numbers.sortDescending() println("Sort into descending: $numbers") // [two, three, one, four] numbers.sortBy { it.length } println("Sort into ascending by length: $numbers") // [two, one, four, three] numbers.sortByDescending { it.last() } println("Sort into descending by the last letter: $numbers") // [four, two, one, three] numbers.sortWith(compareBy<String> { it.length }.thenBy { it }) println("Sort by Comparator: $numbers") // [one, two, four, three] numbers.shuffle() println("Shuffle: $numbers") // [three, one, two, four] numbers.reverse() println("Reverse: $numbers") // [four, two, one, three]
Kotlin 集合包中包含 set 常用操作的扩展函数:查找交集、并集或差集。
要将两个集合合并为一个(并集),可使用 union() 函数。也能以中缀形式使用 a union b。 注意,对于有序集合,操作数的顺序很重要:在结果集合中,左侧操作数在前。
要查找两个集合中都存在的元素(交集),请使用 intersect() 。 要查找另一个集合中不存在的集合元素(差集),请使用 subtract() 。 这两个函数也能以中缀形式调用,例如, a intersect b 。
val numbers = setOf("one", "two", "three") Log.d("TAG","${numbers union setOf("four", "five")}") // [one, two, three, four, five] Log.d("TAG","${setOf("four", "five") union numbers}") // [four, five, one, two, three] Log.d("TAG","${numbers intersect setOf("two", "one")}") // [one, two] Log.d("TAG","${numbers subtract setOf("three", "four")}") // [one, two]
注意, List 也支持 Set 操作。 但是,对 List 进行 Set 操作的结果仍然是 Set ,因此将删除所有重复的元素。
val numbers = listOf("one", "two", "three") Log.d("TAG","${numbers union listOf("three","four","five")}") //[one, two, three, four, five] Log.d("TAG","${listOf("four","five","two") union numbers}") //[four, five, two, one, three] Log.d("TAG","${numbers intersect listOf("two", "one")}") // [one, two] Log.d("TAG","${numbers subtract listOf("three", "four")}") // [one, two]
在 map 中,键和值的类型都是用户定义的。 对基于键的访问启用了各种特定于 map 的处理函数,从键获取值到对键和值进行单独过滤。 在章节上,我们提供了来自标准库的 map 处理功能的描述。
get()、getValue() 、getOrElse()、getOrDefault()
要从 Map 中检索值,必须提供其键作为 get() 函数的参数。 还支持简写 [key] 语法。 如果找不到给定的键,则返回 null 。
还有一个函数 getValue() ,它的行为略有不同:如果在 Map 中找不到键,则抛出异常。
此外,还有两个选项可以解决键缺失的问题:
getOrElse() 与 list 的工作方式相同:对于不存在的键,其值由给定的 lambda 表达式返回。
getOrDefault() 如果找不到键,则返回指定的默认值。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.get("one")) // 1 println(numbersMap["one"]) // 1 println(numbersMap.getOrDefault("four", 10)) // 10 println(numbersMap["five"]) // null numbersMap.getValue("six") // NoSuchElementException 抛出异常
要对 map 的所有键或所有值执行操作,可以从属性 keys 和 values 中相应地检索它们。 keys 是 Map 中所有键的集合, values 是 Map 中所有值的集合。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.keys) // [one, two, three] println(numbersMap.values) // [1, 2, 3]
filter()
可以使用 filter() 函数来过滤 map 或其他集合。 对 map 使用 filter() 函数时, Pair 将作为参数的谓词传递给它。 它将使用谓词同时过滤其中的键和值。
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) // {key11=11}
filterKeys() 按键、filterValues() 按值
还有两种用于过滤 map 的特定函数:按键或按值。 这两种方式,都有对应的函数: filterKeys() 和 filterValues() 。 两者都将返回一个新 Map ,其中包含与给定谓词相匹配的条目。 filterKeys() 的谓词仅检查元素键, filterValues() 的谓词仅检查值。
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) // {key1=1, key11=11} println(filteredValuesMap) // {key1=1, key2=2, key3=3}
由于需要访问元素的键,plus(+)与 minus(-)运算符对 map 的作用与其他集合不同。
plus 返回包含两个操作数元素的 Map :左侧的 Map 与右侧的 Pair 或另一个 Map 。 当右侧操作数中有左侧 Map 中已存在的键时,该条目将使用右侧的值。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap + Pair("four", 4)) // {one=1, two=2, three=3, four=4} println(numbersMap + Pair("one", 10)) // {one=10, two=2, three=3} println(numbersMap + mapOf("five" to 5, "one" to 11)) // {one=11, two=2, three=3, five=5}
minus 将根据左侧 Map 条目创建一个新 Map ,右侧操作数带有键的条目将被剔除。 因此,右侧操作数可以是单个键或键的集合: list 、 set 等。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap - "one") // {two=2, three=3} println(numbersMap - listOf("two", "four")) // {one=1, three=3}
关于在可变 Map 中使用 plusAssign(+=)与 minusAssign(-=)运算符的详细信息,请参见 Map 写操作(第16.4章节)
Mutable Map (可变 Map )提供特定的 Map 写操作。 这些操作使你可以使用键来访问或更改 Map 值。
Map 写操作的一些规则:
下面是对可变 Map 中可用写操作的标准库函数的描述。
put()
要将新的键值对添加到可变 Map ,请使用 put() 。 将新条目放入 LinkedHashMap (Map的默认实现)后,会添加该条目,以便在 Map 迭代时排在最后。 在 Map 类中,新元素的位置由其键顺序定义。
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap.put("three", 3) println(numbersMap) // {one=1, two=2, three=3}
putAll()
要一次添加多个条目,请使用 putAll() 。它的参数可以是 Map或一组 Pair:Iterable 、 Sequence 或 Array 。
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.putAll(setOf("four" to 4, "five" to 5)) println(numbersMap) // {one=1, two=2, three=3, four=4, five=5}
如果给定键已存在于 Map 中,则 put() 与 putAll() 都将覆盖值。 因此,可以使用它们来更新 Map 条目的值。
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap.put("one", 11) println(numbersMap) // {one=11, two=2}
plusAssign()、set()
还可以使用快速操作符将新条目添加到 Map 。 有两种方式:
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap["three"] = 3 // 调用 numbersMap.set("three", 3) numbersMap += mapOf("four" to 4, "five" to 5) println(numbersMap) // {one=1, two=2, three=3, four=4, five=5}
使用 Map 中存在的键进行操作时,将覆盖相应条目的值。
remove()
要从可变 Map 中删除条目,请使用 remove() 函数。 调用 remove() 时,可以传递键或整个键值对。 如果同时指定键和值,则仅当键值都匹配时,才会删除此的元素。
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.remove("one") println(numbersMap) // {two=2, three=3} numbersMap.remove("three", 4) //不会删除任何条目 println(numbersMap) // {two=2, three=3}
还可以通过键或值从可变 Map 中删除条目。 在 Map 的 .keys 或 .values 中调用 remove() 并提供键或值来删除条目。 在.values 中调用时, remove() 仅删除给定值匹配到的的第一个条目。
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3, "threeAgain" to 3) numbersMap.keys.remove("one") println(numbersMap) // {two=2, three=3, threeAgain=3} numbersMap.values.remove(3) println(numbersMap) // {two=2, threeAgain=3}
minusAssign (-=)
minusAssign (-=) 操作符也可用于可变 Map 。
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap -= "two" println(numbersMap) // {one=1, three=3} numbersMap -= "five" //不会删除任何条目 println(numbersMap) // {one=1, three=3}
原创声明,本文系作者授权云+社区发表,未经许可,不得转载。
如有侵权,请联系 yunjia_community@tencent.com 删除。
我来说两句