Skip to content

进阶集合操作

源:Kotlin standard library - Collections

Kotlin 集合库不仅仅是 Java java.util 的包装,它通过内联函数提供了极其强大的声明式操作。掌握这些进阶操作可以显著减少模板代码,并提高代码的可读性和执行效率。

集合构建器 (Collection Builders) Kotlin 1.6+

如果你需要创建一个复杂的只读集合,通常的做法是先创建一个 MutableList,填充数据后再调用 toList()。使用 buildList 可以更优雅地完成这一切。

kotlin
val activeUsers = buildList {
    add("Alice")
    if (includeAdmin) add("Admin")
    addAll(otherUsers.filter { it.isActive })
    // 块结束时自动转换为不可变的 List
}
kotlin
val permissions = buildMap<String, Int> {
    put("read", 1)
    put("write", 2)
    if (isSuperUser) put("delete", 4)
}

分组与聚合 (Grouping)

groupingBy:高性能聚合

groupBy 会直接生成一个 Map<K, List<V>>,如果你只是为了对每个分组进行计数或求和,groupBy 会产生不必要的中间 List 对象。groupingBy 配合 eachCountfold 则更加高效。

kotlin
val words = "one two one three two one".split(" ")

// ❌ 效率较低:创建了多个中间 List
val counts1 = words.groupBy { it }.mapValues { it.value.size }

// ✅ 推荐做法:不创建中间 List
val counts2 = words.groupingBy { it }.eachCount()
kotlin
val orders = listOf(Order("A", 10), Order("B", 20), Order("A", 5))

// 按类别累加金额
val totals = orders.groupingBy { it.category }
    .fold(0) { accumulator, order ->
        accumulator + order.amount
    }

窗口与分块 (Windowing & Chunking)

在处理连续数据(如折线图平滑、滑动平均值)时,这两个函数非常有用。

chunked:分批处理

kotlin
val numbers = (1..10).toList()

// 每 3 个一组
val chunks = numbers.chunked(3) 
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

// 带转换操作
numbers.chunked(3) { it.sum() } // [6, 15, 24, 10]

windowed:滑动窗口

kotlin
val sequence = listOf(1, 2, 3, 4, 5)

// 窗口大小 3,步长 1
val windows = sequence.windowed(3)
// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

// 窗口大小 3,步长 3 (等同于 chunked)
val windows2 = sequence.windowed(3, step = 3, partialWindows = true)

关联与分区 (Associating & Partition)

associate 系列:构建 Map 的利器

kotlin
// Key 转换:使用对象属性作为 Key
val userMap = users.associateBy { it.id } 
// Map<Id, User>
kotlin
// Value 转换:保持原对象为 Key,计算新值为 Value
val userRoles = users.associateWith { getRole(it) } 
// Map<User, Role>
kotlin
// 自定义 Key-Value 对
val nameToLength = users.associate { it.name to it.name.length }

partition:一分为二

当你需要根据条件将集合拆分为“符合条件”和“不符合条件”的两部分时,partition 比两次 filter 更高效且逻辑更清晰。

kotlin
val (adults, minors) = users.partition { it.age >= 18 }

println("成年人: $adults")
println("未成年: $minors")

集合操作符对比表

操作符结果类型说明
zipList<Pair<T, R>>将两个集合按索引配对成 Pair
zipWithNextList<Pair<T, T>>将集合中相邻的元素两两配对
flatMapList<R>转换每个元素为集合,并将所有结果展平
flattenList<T>List<List<T>> 展平为 List<T>
scanList<R>类似 fold,但会保留每一个中间计算结果

性能准则

  1. 优先使用 Sequence:如果链式操作超过 3 个,且集合数据量较大,请务必使用 .asSequence() 开启惰性计算。
  2. 避免反复创建 Mutable 集合:在循环中使用 += 操作 List 会导致频繁的数组拷贝。应优先使用 buildListtoMutableList() 后再一次性转换。
  3. 注意 Iterable vs Sequence
    • Iterable:每一步都会创建一个新的中间集合。
    • Sequence:只有在遇到终结操作(如 toList(), first())时才会执行计算。