kotlin的集合操作

1,084 阅读7分钟

想要定义 kotlin 的集合比较简单, 你想要什么集合, 就给什么集合后面添加 Of 就出来了, setOf, hashSetOf, ArrayListOf, listOf, MapOf... 所以本文不讲如何创建集合了

集合转换

把旧集合通过某种方式转化成新的集合

map 映射

map 和 mapTo: 拿出一个个元素转化成新的

image.png

转化, 把 T 转化 成 R, 然后再存入到一个新的集合中

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
    for (item in this)
        // ******************
        destination.add(transform(item))
    return destination
}

destination: 是 ArrayList 类型 transform: 是转化函数

val list = (1..10).toMutableList()
val map: List<Double> = list.map {
   it * 0.9
}
println(map) // [0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.0]

mapIndexed 带索引的映射

image.png

源码:

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapIndexedTo(destination: C, transform: (index: Int, T) -> R): C {
    var index = 0
    for (item in this)
        // ******************
        destination.add(transform(checkIndexOverflow(index++), item))
    return destination
}
val indexed = list.mapIndexed { index, i ->
   "k: $index v: $i"
}
// [k: 0 v: 1, k: 1 v: 2, k: 2 v: 3, k: 3 v: 4, k: 4 v: 5, k: 5 v: 6, k: 6 v: 7, k: 7 v: 8, k: 8 v: 9, k: 9 v: 10]
println(indexed)
val notNullList: List<Byte> = list.mapNotNull {
   if (it % 2 == 0) {
      null
   } else {
      it.toByte()
   }
}
// [1, 3, 5, 7, 9]
println(notNullList)

zip 多个集合整合成一个

zip pair

源码:

public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V> {
    val first = iterator()
    val second = other.iterator()
    val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
    while (first.hasNext() && second.hasNext()) {
        // ******************
        list.add(transform(first.next(), second.next()))
    }
    return list
}

根据方法简单的分析, 发现 T 发起的 zip合拢 操作, 将 R 类型的集合的数据, 借助 transform 函数, 将 TR 传入 转化处新的类型 V, 而返回的接口类型是 List<V>ArrayList 类型

使用方法:

val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(animals zip colors) // [(fox, red), (bear, brown), (wolf, grey)]

val twoAnimal = listOf("fox", "bear")
colors.zip(twoAnimal) // [(red, fox), (brown, bear)]

上面这段代码走的是 zip(other) { t1, t2 -> t1 to t2 } 也就是说, 默认走的是 Pair, 也就是说将 Pair 存入到 ArrayList

zip 自定义方法拦截映射

那么现在给个自定义方式的 zip, public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V>

使用方法:

val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors.zip(animals) { color, animal ->
   "^$color, $animal^"
})

unzip 解开 pair

unzip 函数: 解开 Pairfirstsecond

源码:

public fun <T, R> Iterable<Pair<T, R>>.unzip(): Pair<List<T>, List<R>> {
    val expectedSize = collectionSizeOrDefault(10)
    val listT = ArrayList<T>(expectedSize)
    val listR = ArrayList<R>(expectedSize)
    for (pair in this) {
        // ******************
        listT.add(pair.first)
        listR.add(pair.second)
    }
    return listT to listR
}

使用方法:

val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
val pair = numberPairs.unzip()
println(pair.first) // [one, two, three, four]
println(pair.second) // [1, 2, 3, 4]

associate 关联: 把元素拆分成两个存放到新的集合中

associate

image.png

看起来就是把 T 按照某种方式分割成 kv 然后存储到 Pair

使用方式:

val numbers = listOf("one", "two", "three", "four")
val associate = numbers.associate { Pair(it.length, it) }
println(associate) // {3=two, 5=three, 4=four}

associateTo(LinkedHashMap<K, V>(capacity), transform) 他的返回值为 LinkedHashMap

源码:

public inline fun <T, K, V, M : MutableMap<in K, in V>> Iterable<T>.associateTo(destination: M, transform: (T) -> Pair<K, V>): M {
    for (element in this) {
        // ******************
        destination += transform(element)
    }
    return destination
}

看其源码果然这样

不过值得注意的是 associateTo

associateTo: 提供集合和自定义拆解方法

val numbers = listOf("one", "two", "three", "four")
val hashMap = numbers.associateTo(HashMap(10)) {
   Pair(it.length, it)
}
println(hashMap) // {3=two, 5=three, 4=four}

associateBy: 借助集合元素获得 Key

val numbers = listOf("one", "two", "three", "four")
val associateBy = numbers.associateBy { it.length }
println(associateBy) // {3=two, 5=three, 4=four}

源码:

public inline fun <T, K, M : MutableMap<in K, in T>> Iterable<T>.associateByTo(destination: M, keySelector: (T) -> K): M {
    for (element in this) {
        // ******************
        destination.put(keySelector(element), element)
    }
    return destination
}

associateWith: 根据集合元素的值计算出 value

image.png

val numbers = listOf("one", "two", "three", "four")
val associateWith = numbers.associateWith {
   it.length
}
println(associateWith) // {one=3, two=3, three=5, four=4}

源码:

public inline fun <K, V, M : MutableMap<in K, in V>> Iterable<K>.associateWithTo(destination: M, valueSelector: (K) -> V): M {
    for (element in this) {
        // ******************
        destination.put(element, valueSelector(element))
    }
    return destination
}

associateBy: 自定义 key 和 自定义 value 方式

image.png

val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy({ it.length }, { it.toUpperCase() })) // {3=TWO, 5=THREE, 4=FOUR}

源码:

public inline fun <T, K, V> Iterable<T>.associateBy(keySelector: (T) -> K, valueTransform: (T) -> V): Map<K, V> {
    val capacity = mapCapacity(collectionSizeOrDefault(10)).coerceAtLeast(16)
    return associateByTo(LinkedHashMap<K, V>(capacity), keySelector, valueTransform)
}

public inline fun <T, K, V, M : MutableMap<in K, in V>> Iterable<T>.associateByTo(destination: M, keySelector: (T) -> K, valueTransform: (T) -> V): M {
    for (element in this) {
    // 关键代码: 
        destination.put(keySelector(element), valueTransform(element))
    }
    return destination
}

flatten: 将集合的子集合全部拿出来创建成新的集合

List<List<String>>

val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
val flatten = numberSets.flatten()
println(flatten) // [1, 2, 3, 4, 5, 6, 1, 2]

源码:

public fun <T> Iterable<Iterable<T>>.flatten(): List<T> {
    val result = ArrayList<T>()
    for (element in this) {
        result.addAll(element)
    }
    return result
}

flatMap: 将子List拿出来addAll到一个新的集合中

image.png

val containers = listOf(
   listOf("one", "two", "three"),
   listOf("four", "five", "six"),
   listOf("seven", "eight")
)
val flatMap = containers.flatMap {
   it
}
println(flatMap) // [one, two, three, four, five, six, seven, eight]

源码:

public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
    return flatMapTo(ArrayList<R>(), transform)
}

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
    for (element in this) {
        val list = transform(element)
        destination.addAll(list)
    }
    return destination
}

其他的函数就不演示了, 和上面的 Map 和 associate 差不多

image.png

joinToString: 字符串打印方式自定义

image.png

val numbers = listOf("one", "two", "three", "four")
val joinToString =
   numbers.joinToString(separator = "^", prefix = "#", postfix = "#", transform = { it.toUpperCase() })
println(joinToString) // #ONE^TWO^THREE^FOUR#

源码:

public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
    return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}

public fun <T, A : Appendable> Iterable<T>.joinTo(buffer: A, separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): A {
    // buffer ==> StringBuilder
    buffer.append(prefix)
    var count = 0
    for (element in this) {
        if (++count > 1) buffer.append(separator)
        if (limit < 0 || count <= limit) {
            buffer.appendElement(element, transform)
        } else break
    }
    // 如果 count 大于 limit 的话, 直接加入阶段字符串
    if (limit >= 0 && count > limit) buffer.append(truncated)
    // 加入结尾标志
    buffer.append(postfix)
    return buffer
}

过滤

根据接受的lambda返回的 true or false 来产生新的集合

filter: 分析每个元素是否满足某种条件

image.png

从遍历元素, 然后判断元素是否满足某种条件返回 boolean 类型

val numbers = listOf("one", "two", "three", "four")
println(numbers.filter { it.length > 3 }) // [three, four]

过滤 map 的条件是否满足

image.png

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.filter { (key, value) ->
   key.endsWith("1") && value > 3
}) // {key11=11}

filterIsInstance: 根据类型过滤

val numbers = listOf(1, "two", 3.0, "four")
val list = numbers.filterIsInstance<Double>()
println(list)

源码:

image.png

partition: 根据某种条件分割一个集合为多个集合

val numbers = listOf("one", "two", "three", "four")
val partition = numbers.partition { it.length > 3 }
println(partition.first)
println(partition.second)

源码:

public inline fun <T> Iterable<T>.partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>> {
    val first = ArrayList<T>()
    val second = ArrayList<T>()
    for (element in this) {
        // 核心代码在这里
        if (predicate(element)) {
            first.add(element)
        } else {
            second.add(element)
        }
    }
    return Pair(first, second)
}

any all none 验证集合是否满足某个条件

val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.startsWith("t") }) // true 只要有一个元素满足条件就返回true
println(numbers.none { it.length < 2 }) // 如果有一个条件满足就返回 false , 否则返回 true
println(numbers.all { it.length == 3 }) // 所有都要满足条件才会返回 true, 否则返回false

groupby 分组

image.png

可以根据集合中的元素计算出分组的key, 然后存放满足该keyList

val numbers = listOf("one", "two", "three", "four", "five")
val groupBy = numbers.groupBy { it.length }.toSortedMap { o1, o2 -> o1 - o2 }
println(groupBy) // {3=[one, two], 4=[four, five], 5=[three]}

val groupBy1 = numbers.groupBy({ it.length }) {
   it.toUpperCase()
}
println(groupBy1) // {3=[ONE, TWO], 5=[THREE], 4=[FOUR, FIVE]}

grouping: 根据 value 获得 key

val numbers = listOf("one", "two", "three", "four", "five", "six")
val grouping = numbers.groupingBy { it.length }
println(grouping.keyOf("six")) // 3
println(grouping.keyOf("three")) // 5

取集合的⼀部分

Slice: 取一部分集合的元素(索引从0开始)

val numbers = listOf("one", "two", "three", "four", "five", "six")
val slice = numbers.slice(0..3) // [one, two, three, four]
println(slice)
val slice1 = numbers.slice(3..5) // [four, five, six]
println(slice1)

take: 从头开始取前n个元素

val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.take(1)) // [one]
println(numbers.take(2)) // [one, two]
println(numbers.take(6)) // [one, two, three, four, five, six]

drop: 从头开始删除前n个元素

val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.drop(1)) // 从前面开始删除前1个
println(numbers.drop(2)) // 从前面开始删除前2个

Chunked: 分块

/*
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13]]
[3, 12, 21, 30, 25]
 */
val numbers = (0..13).toList()
val chunked: List<List<Int>> = numbers.chunked(3)
println(chunked) // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13]]
val chunked1 = numbers.chunked(3) { list ->
   list.sum()
}
println(chunked1) // [3, 12, 21, 30, 25]

Windowed: 步长自定义的分块函数

val numbers = listOf("one", "two", "three", "four", "five")
val windowed = numbers.windowed(3, step = 1, partialWindows = false) { list ->
   list.map { it.toUpperCase() }
}
println(windowed) // [[ONE, TWO, THREE], [TWO, THREE, FOUR], [THREE, FOUR, FIVE]]

subList: 从集合里找一个子集返回

val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.javaClass) // class java.util.Arrays$ArrayList
val subList = numbers.subList(0, 2)
println(subList.javaClass) // class java.util.AbstractList$RandomAccessSubList
println(subList) // [one, two]

取单个元素

elementAt: 按位置取

val numbers = linkedSetOf("one", "two", "three", "four", "five")
println(numbers.elementAt(0)) // one
println(numbers.elementAt(3)) // four

first 和 last

val numbers = linkedSetOf("one", "two", "three", "four", "five")
println(numbers.first()) // one
println(numbers.last()) // five

first and last: 按条件取一个

val numbers = linkedSetOf("one", "two", "three", "four", "five")
println(numbers.first { it.length == 4 }) // four

find: 根据条件取一个

val numbers = listOf(1, 2, 3, 4)
println(numbers.find { it > 3 }) // 4

random: 随机取一个

val number = listOf(1, 2, 3, 4)
println(number.random()) // 2

检测存在与否

val numbers = listOf(1, 2, 3, 4)
println(numbers.contains(2))
println(numbers.containsAll(listOf(3, 4)))

还有 isEmptyisNotEmpty 就不做测试了

集合排序

Comparable 主要给类实现用

class Program(val version: Int) : Comparable<Int> {
   override fun compareTo(other: Int): Int = this.version - other
}

fun main() {
   val program = Program(13)
   println(program > 10)
   println(program > 13)
   println(program < 20)
}

还可以不使用Comparable

class Program(val version: Int) {
   operator fun compareTo(program2: Program): Int {
      return this.version - program2.version
   }
   
   operator fun compareTo(i: Int): Int {
      return this.version - i
   }
}

fun main() {
   val program1 = Program(13)
   val program2 = Program(14)
   println(program1 > program2)
   program1 > 14
}

倒序和随机顺序

val numbers = listOf("one", "two", "three", "four")
println(numbers) // [one, two, three, four]
println(numbers.reversed()) // [four, three, two, one]
println(numbers.asReversed()) // [four, three, two, one]
println(numbers.shuffled()) // [four, one, three, two]

集合聚合操作

val numbers = listOf(6, 42, 10, 4)
println("Count: ${numbers.count()}") // Count: 4
println("Max: ${numbers.maxOrNull()}") // Max: 42
println("Min: ${numbers.minOrNull()}") // Min: 4
println("Average: ${numbers.average()}") // Average: 15.5
println("Sum: ${numbers.sum()}") // Sum: 62

fold: 有初始化值R和集合T的某种组合的结果R返回回去

image.png

reduce: 把集合的第一个值当作S, 然后和集合的每个元素T做某种操作最后返回 S

image.png

set相关操作

联合 union 、 交集 intersect 和 差集 subtract

val numbers = setOf("one", "two", "three")
// 合并两个集合
println(numbers union setOf("four", "five")) // [one, two, three, four, five]
// 找交集
println(numbers intersect setOf("one")) // [one] 
// 差集
println(numbers subtract setOf("one", "three")) // [two]