Kotlin学习笔记17——集合公共操作

前言

上一篇我们学习了Kotlin中的集合的概述,今天继续来学习Kotlin中的集合。集合的内容包含的比较多,分为三篇来学习,今天是第二篇我们学习的集合的操作。

集合操作概述

Kotlin 标准库提供了用于对集合执行操作的多种函数。这包括简单的操作,例如获取或添加元素,以及更复杂的操作,包括搜索、排序、过滤、转换等。

公共操作

公共操作可用于只读集合与可变集合:

  • 只读集合 (List,Map,Set)
  • 可变集合(MutableList,MutableMap,MutableSet)

集合转换

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 中的键或值。

  • associateWith() :创建一个 Map,其中原始集合的元素是键,并通过给定的转换函数从中产生值。 如果两个元素相等,则仅最后一个保留在 Map 中。
  • associateBy():它需要一个函数,该函数根据元素的值返回键。如果两个元素相等,则仅最后一个保留在 Map 中。
  • associate():其中 Map 键和值都是通过集合元素生成的。 它需要一个 lambda 函数,该函数返回 Pair:键和相应 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中提供了一些标准库函数:

  • flatten():可以在一个集合的集合(例如,一个 Set 组成的 List)上调用它。 该函数返回嵌套集合中的所有元素的一个 List。
  • flatMap():它需要一个函数将一个集合元素映射到另一个集合。 因此,flatMap() 返回单个列表其中包含所有元素的值。 所以,flatMap() 表现为 map()(以集合作为映射结果)与 flatten() 的连续调用。
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提供了一些标准库函数:

  • joinToString() : 根据提供的参数从集合元素构建单个 String。
  • joinTo():执行和 joinToString()相同的操作,但将结果附加到给定的 Appendable 对象。
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中提供了一下标准库函数 :

  • filter():只能检查元素的值。
  • filterIndexed():过滤中使用元素在集合中的位置。
  • filterNot():使用否定条件来过滤集合。
  • filterIsInstance<T>() :返回给定类型的集合元素。
  • filterNotNull():返回所有的非空元素。
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 来说长度不可用
}

划分

  • partition() : 通过一个谓词过滤集合并且将不匹配的元素存放在一个单独的列表中。

因此,你得到一个 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)

检验

有些函数只是针对集合元素简单地检测:

  • any():如果至少有一个元素匹配给定谓词,那么 any() 返回 true。
  • none() :如果没有元素与给定谓词匹配,那么 none() 返回 true。
  • all():如果所有元素都匹配给定谓词,那么 all() 返回 true。注意,在一个空集合上使用任何有效的谓词去调用 all() 都会返回 true 。
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 (-) 操作符。 它们把一个集合作为第一个操作数;第二个操作数可以是一个元素或者是另一个集合。 返回值是一个新的只读集合:

  • plus:结果包含原始集合 和 第二个操作数中的元素。
  • minus:结果包含原始集合中的元素除去第二个操作数中的元素 。 如果第二个操作数是一个元素,那么 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

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 与 drop

要从头开始获取指定数量的元素,请使用 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():是带有谓词的 take():它将不停获取元素直到排除与谓词匹配的首个元素。如果首个集合元素与谓词匹配,则结果为空。
  • takeLastWhile() :与 takeLast() 类似:它从集合末尾获取与谓词匹配的元素区间。区间的首个元素是与谓词不匹配的最后一个元素右边的元素。如果最后一个集合元素与谓词匹配,则结果为空。
  • dropWhile() :与具有相同谓词的 takeWhile() 相反:它将首个与谓词不匹配的元素返回到末尾。
  • dropLastWhile() :与具有相同谓词的 takeLastWhile() 相反:它返回从开头到最后一个与谓词不匹配的元素。
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

chunked() 函数要将集合分解为给定大小的“块”。

val numbers = (0..13).toList()
println(numbers.chunked(3))
println(numbers.chunked(3) { it.sum() })// `it` 为原始集合的一个块

Windowed

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。

按位置取

  • elementAt(index):检索特定位置的元素。
  • first():检索集合的第一个元素。
  • last():检索集合的最后一个元素。
  • elementAtOrNull(index):当指定位置超出集合范围时,elementAtOrNull() 返回 null。
  • elementAtOrElse():当使用一个越界位置来调用时,elementAtOrElse() 返回对给定值调用该 lambda 表达式的结果。
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())

检测存在与否

  • contains() :检查集合中某个元素是否存在。
  • containsAll():一次检查多个实例的存在。
  • isEmpty() 和 isNotEmpty() :检查集合中是否包含任何元素。

排序

自然顺序(字典排序,或者数值大小排序)

基本的函数 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 集合包含用于常用的 聚合操作 (基于集合内容返回单个值的操作)的函数 。

  • min() /max() :分别返回最小和最大的元素。
  • average() :返回数字集合中元素的平均值。
  • sum():返回数字集合中元素的总和。
  • count():返回集合中元素的数量。
  • maxBy()/minBy() :接受一个选择器函数并返回使选择器返回最大或最小值的元素。
  • sumBy():使用对集合元素调用返回 Int 值的函数。
  • sumByDouble() :与返回 Double 的函数一起使用。
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

Fold 与 reduce

们依次将所提供的操作应用于集合元素并返回累积的结果

  • fold():接受一个初始值并将其用作第一步的累积值
  • reduce():第一步则将第一个和第二个元素作为第一步的操作参数。
  • reduceRight():类似于reduce(),但从最后一个元素开始,然后再继续到前一个元素。
  • foldRight():类似于fold(),但从最后一个元素开始,然后再继续到前一个元素。
  • reduceIndexed()/foldIndexed():传递元素索引作为操作的第一个参数。
  • reduceRightIndexed() /foldRightIndexed():将这些操作从右到左应用于集合元素。

记住,在使用 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 的所有写操作。

添加元素

  • add():将单个元素添加到列表或集合,指定的对象将添加到集合的末尾。
  • addAll():将参数对象的每个元素添加到列表或集合中。参数可以是 Iterable、Sequence 或 Array。 接收者的类型和参数可能不同,例如,你可以将所有内容从 Set 添加到 List。
  • plus(+)运算符:等同于add()
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]

删除元素

  • remove():从可变集合中移除元素。
  • removeAll(): 移除参数集合中存在的所有元素。 或者,你可以用谓词(lamada里面加条件)作为参数来调用它;在这种情况下,函数移除谓词产生 true 的所有元素。
  • retainAll() :与 removeAll() 相反:它移除除参数集合中的元素之外的所有元素。 当与谓词一起使用时,它只留下与之匹配的元素。
  • clear() :从列表中移除所有元素并将其置空。
  • minusAssign (-=):效果等同于remove()
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相关操作
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值