上一篇我们学习了Kotlin中的集合的概述,今天继续来学习Kotlin中的集合。集合的内容包含的比较多,分为三篇来学习,今天是第二篇我们学习的集合的操作。
Kotlin 标准库提供了用于对集合执行操作的多种函数。这包括简单的操作,例如获取或添加元素,以及更复杂的操作,包括搜索、排序、过滤、转换等。
公共操作可用于只读集合与可变集合:
Kotlin 标准库为集合 转换 提供了一组扩展函数。 这些函数根据提供的转换规则从现有集合中构建新集合。
基本的映射函数是 map(),它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。 结果的顺序与元素的原始顺序相同。 如需应用还要用到元素索引作为参数的转换,请使用 mapIndexed(),参考如下代码:
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })//使用map映射
println(numbers.mapIndexed { idx, value -> value * idx })
如果转换在某些元素上产生 null 值,则可以通过调用 mapNotNull() 函数取代 map() 或 mapIndexedNotNull() 取代 mapIndexed() 来从结果集中过滤掉 null 值。
val numbers = setOf(1, 2, 3)
//过滤掉第二个元素
println(numbers.mapNotNull { if ( it == 2) null else it * 3 })//结果:[3, 9]
//过滤掉第一个元素
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })//结果:[2, 6]
映射转换时,有两个选择:转换键,使值保持不变,反之亦然。 要将指定转换应用于键,请使用 mapKeys();反过来,mapValues() 转换值。 这两个函数都使用将映射条目作为参数的转换,因此可以操作其键与值。
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.toUpperCase() })//结果:{KEY1=1, KEY2=2, KEY3=3, KEY11=11}
println(numbersMap.mapValues { it.value + it.key.length })//结果:{key1=5, key2=6, key3=7, key11=16}
双路合并 转换是根据两个集合(Iterable,不包括Map集合)中具有相同位置的元素构建配对。 在 Kotlin 标准库中,这是通过 zip() 扩展函数完成的。 zip() 返回 Pair 对象的列表(List)。 接收者集合的元素是这些配对中的第一个元素。 如果集合的大小不同,则 zip() 的结果为较小集合的大小;结果中不包含较大集合的后续元素。 zip() 也可以中缀形式调用 a zip b 。
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors zip animals)//结果:[(red, fox), (brown, bear), (grey, wolf)]
val twoAnimals = listOf("fox", "bear")
//集合大小不同,结果为较小集合大小,且不包含较大集合后续元素
println(colors.zip(twoAnimals))//结果:[(red, fox), (brown, bear)]
也可以使用带有两个参数的转换函数来调用 zip():接收者元素和参数元素。 在这种情况下,结果 List 包含在具有相同位置的接收者对和参数元素对上调用的转换函数的返回值。
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
//结果:[The Fox is red, The Bear is brown, The Wolf is grey]
println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"})
当拥有 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])
关联 转换允许从集合(Iterable,不包括Map集合)元素和与其关联的某些值构建 Map。 在不同的关联类型中,元素可以是关联 Map 中的键或值。
val numbers = listOf("one", "two", "three", "four")
//打印:{one=3, two=3, three=5, four=4}
println(numbers.associateWith { it.length })
val numbers = listOf("one", "two", "three", "four")
//打印:{O=one, T=three, F=four}
println(numbers.associateBy { it.first().toUpperCase() })
//打印:{O=3, T=5, F=4} 这里keySelector valueTransform 为固定写法
println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
//打印:{Adams=Alice, Brown=Brian, Campbell=Clara}
println(names.associate { name -> name.split(" ").let { it[0] to it[1] } })
需要注意的是:associate() 会生成临时的 Pair 对象,这可能会影响性能。 因此,当性能不是很关键或比其他选项更可取时,应使用 associate()。
打平就是对嵌套的集合(Iterable,不包括Map集合)进行合并。Kotlin中提供了一些标准库函数:
val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
//打印结果:[1, 2, 3, 4, 5, 6, 1, 2]
println(numberSets.flatten())
val containers = listOf(mapOf(1 to "one",2 to "two", 3 to "three"),mapOf(4 to "four",5 to "five", 6 to "six"))
//打印结果:[one, two, three, four, five, six]
println(containers.flatMap { it.values })
将集合转换为字符串,Kotlin提供了一些标准库函数:
val numbers = listOf("one", "two", "three", "four")
println(numbers) //结果:[one, two, three, four]
println(numbers.joinToString())//结果:one, two, three, four
val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)//结果:The list of numbers: one, two, three, four
要构建自定义字符串表示形式,可以在函数参数 separator、prefix 与 postfix中指定其参数。 结果字符串将以 prefix 开头,以 postfix 结尾。除最后一个元素外,separator 将位于每个元素之后。
val numbers = listOf("one", "two", "three", "four")
//打印结果;start: one | two | three | four: end
println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
对于较大的集合,可能需要指定 limit ——将包含在结果中元素的数量。 如果集合大小超出 limit,所有其他元素将被 truncated 参数的单个值替换。
val numbers = (1..100).toList()
//打印结果:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, <...>
println(numbers.joinToString(limit = 10, truncated = "<...>"))
Kotlin标准库包含了一组让你能够通过单个调用就可以过滤集合的扩展函数。这些函数不会改变原始集合,因此它们既可用于可变集合也可用于只读集合。为了操作过滤结果,应该在过滤后将其赋值给变量或链接其他函数。
当使用一个谓词(接受一个集合元素并且返回布尔值的 lambda 表达式:true 说明给定元素与谓词匹配,false 则表示不匹配)来调用时,filter() 返回与其匹配的集合元素。对于 List 和 Set,过滤结果都是一个 List,对 Map 来说结果还是一个 Map。Kotlin中提供了一下标准库函数 :
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 }
//打印结果:[three, four]
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}
//打印结果:{key11=11}
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 }
//打印结果:[two, four]
println(filteredIdx)
//打印结果:[three, four]
println(filteredNot)
val numbers = listOf(null, 1, "two", 3.0, "four")
println("All String elements in upper case:")
numbers.filterIsInstance<String>().forEach {
println(it.toUpperCase())
}
val numbers = listOf(null, "one", "two", null)
numbers.filterNotNull().forEach {
println(it.length) // 对可空的 String 来说长度不可用
}
因此,你得到一个 List 的 Pair 作为返回值:第一个列表包含与谓词匹配的元素并且第二个列表包含原始集合中的所有其他元素。
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
//打印结果:[three, four]
println(match)
//打印结果:[one, two]
println(rest)
有些函数只是针对集合元素简单地检测:
val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.endsWith("e") })//true
println(numbers.none { it.endsWith("a") })//true
println(numbers.all { it.endsWith("e") })//false
println(emptyList<Int>().all { it > 5 }) //true
为集合定义了 plus (+) 和 minus (-) 操作符。 它们把一个集合作为第一个操作数;第二个操作数可以是一个元素或者是另一个集合。 返回值是一个新的只读集合:
val numbers = listOf("one", "two", "three", "four")
val plusList = numbers + "five"
val minusList = numbers - listOf("three", "four")
println(plusList)//打印结果:[one, two, three, four, five]
println(minusList)//打印结果:[one, two]
var maps = mapOf(1 to 2,2 to 3)
println(maps)//打印结果:{1=2, 2=3}
maps = maps + mapOf(3 to 4)
println(maps)//打印结果:{1=2, 2=3, 3=4}
Kotlin 标准库提供用于对集合元素进行分组的扩展函数。 基本函数 groupBy() 使用一个 lambda 函数并返回一个 Map。 在此 Map 中,每个键都是 lambda 结果,而对应的值是返回此结果的元素 List。 例如,可以使用此函数将 String 列表按首字母分组。
还可以使用第二个 lambda 参数(值转换函数)调用 groupBy()。 在带有两个 lambda 的 groupBy() 结果 Map 中,由 keySelector 函数生成的键映射到值转换函数的结果,而不是原始元素。
val numbers = listOf("one", "two", "three", "four", "five")
//打印结果:{O=[one], T=[two, three], F=[four, five]}
println(numbers.groupBy { it.first().toUpperCase() })
//打印结果:{o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}
println(numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.toUpperCase() }))
Kotlin 标准库包含用于取集合的一部分的扩展函数。 这些函数提供了多种方法来选择结果集合的元素:显式列出其位置、指定结果大小等。
slice() 返回具有给定索引的集合元素列表。 索引既可以是作为区间传入的也可以是作为整数值的集合传入的。
val numbers = listOf("one", "two", "three", "four", "five", "six")
//打印结果:[two, three, four]
println(numbers.slice(1..3))
//打印结果:[one, three, five]
println(numbers.slice(0..4 step 2))
//打印结果:[four, six, one]
println(numbers.slice(setOf(3, 5, 0)))
要从头开始获取指定数量的元素,请使用 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]
还可以使用谓词来定义要获取或去除的元素的数量。 有四个与上述功能相似的函数:
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.takeWhile { !it.startsWith('f') })//打印结果:[one, two, three]
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() 函数要将集合分解为给定大小的“块”。
val numbers = (0..13).toList()
println(numbers.chunked(3))
println(numbers.chunked(3) { it.sum() })// `it` 为原始集合的一个块
windowed()函数可以检索给定大小的集合元素中所有可能区间。
val numbers = listOf("one", "two", "three", "four", "five")
//打印结果:[[one, two, three], [two, three, four], [three, four, five]]
println(numbers.windowed(3))
//提供可选参数step和partialWindows
val numbers = (1..10).toList()
//打印结果:[[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]]
println(numbers.windowed(3, step = 2, partialWindows = true))
Kotlin 集合提供了一套从集合中检索单个元素的函数。 这些函数适用于 list 和 set。
val numbers = linkedSetOf("one", "two", "three", "four", "five")
println(numbers.elementAt(3))
println(numbers.first())
println(numbers.last())
println(numbers.elementAtOrNull(5))//结果:null
//结果:The value for index 5 is undefined
println(numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"})
first() 和 last() 还可以让你在集合中搜索与给定条件匹配的元素。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.first { it.length > 3 })
println(numbers.last { it.startsWith("f") })
如果别名更适合你的情况,那么可以使用别名:
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)
println(numbers.random())
基本的函数 sorted() 和 sortedDescending() 返回集合的元素,这些元素按照其自然顺序升序和降序排序。 这些函数适用于 Comparable 元素的集合。
val numbers = listOf("one", "two", "three", "four")
//结果:Sorted ascending: [four, one, three, two]
println("Sorted ascending: ${numbers.sorted()}")
//结果:Sorted descending: [two, three, one, four]
println("Sorted descending: ${numbers.sortedDescending()}")
reversed() 函数以相反的顺序检索集合。
val numbers = listOf("one", "two", "three", "four")
println(numbers.reversed())//结果:[four, three, two, one]
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)//结果:[four, three, two, one]
reversed() 返回带有元素副本的新集合。 因此,如果你之后改变了原始集合,这并不会影响先前获得的 reversed() 的结果。asReversed()——返回相同集合实例的一个反向视图,因此,如果原始列表不会发生变化,那么它会比 reversed() 更轻量,更合适。
shuffled() 函数返回一个包含了以随机顺序排序的集合元素的新的 List。
val numbers = listOf("one", "two", "three", "four")
println(numbers.shuffled())//打印结果可能每次都不一样
自定义排序通过实现Comparable接口实现:
class TestMain constructor(var name:String) : Comparable<TestMain>{
override fun compareTo(other: TestMain): Int {
val result = this.name.compareTo(other.name)
println("result : $result")
return result //compareTo函数中的逻辑可以根据业务来编写,返回结果大于0,比较结果为true,小于等于0,比较结果为false
}
}
println(TestMain("Jim") > TestMain("Tom"))//false
println(TestMain("Jim") > TestMain("Ami"))//true
Kotlin 集合包含用于常用的 聚合操作 (基于集合内容返回单个值的操作)的函数 。
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
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minBy { it % 3 }
println(min3Remainder)//结果:42,42对3取整为0,最小
val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWith(compareBy { it.length })
println(longestString)//结果:three
val numbers = listOf(5, 42, 10, 4)
println(numbers.sumBy { it * 2 })//结果:122
println(numbers.sumByDouble { it.toDouble() / 2 })//结果:30.5
们依次将所提供的操作应用于集合元素并返回累积的结果
记住,在使用 foldRight 或 reduceRight 时,操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
val numbers = listOf(5, 2, 10, 4)
val sum = numbers.reduce { sum, element -> sum + element }
println(sum)//结果:21
val sumDoubled = numbers.fold(3) { sum, element -> sum + element * 2 }
println(sumDoubled)//结果:45
//这里的第一个参数必须是element,第二个是累加值
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)//结果:15
val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
println(sumEvenRight)//结果:15
可变集合支持更改集合内容的操作,例如添加或删除元素。 在此页面上,我们将描述实现 MutableCollection 的所有写操作。
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.addAll(arrayOf(7, 8))
numbers += 9
println(numbers)//结果:[1, 2, 3, 4, 5, 7, 8, 9]
val numbers = mutableListOf(1, 2, 3, 4, 3)
numbers.remove(3)
numbers -= 4
numbers.retainAll { it >= 3 }
numbers.clear()
文章篇幅有限,这个我们留到下篇在具体学习关于list,map和set的特定操作。
今天的学习笔记就先到这里了,下一篇我们将学习集合中list、map、set相关操作。
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!