研究表明,开发者对Kotlin集合的了解不到 20%

1 阅读5分钟

dig.jpg

HR:你有多少年 Kotlin 使用经验?

候选人:5 年!

双方都挺满意。但你真的觉得,做了五年 Android 开发,就把 Kotlin 用透吗?或许你只用到了它的 20%。

我深入研究 Kotlin 时才发现,我就是个“伪 Kotlin 开发者”——它有太多强大的方法、扩展函数,还有很多我从没接触过的特性。

做算法题时,一看到数组,脑子里第一反应就是写个 for 循环。

但如果你用 Kotlin 解决问题,其实有很多更优雅的替代方案。

这篇文章,我会把 Kotlin 集合的惯用写法过一遍,它们会彻底改变你的代码风格。

Kotlin 标准库提供了极其丰富的集合操作,让你能写出简洁、可读、安全且高效的代码。使用地道的 Kotlin 写法,可以帮你避免样板循环、手动映射、计数器变量等冗余代码。

转换:map 和 flatMap

作用:对集合里的每一个元素进行转换。

val names = listOf("Alice", "Bob")
val upper = names.map { it.uppercase() } // [ALICE, BOB]
val indexed = names.mapIndexed { i, n -> "$i:$n" } // [0:Alice, 1:Bob]

val nested = listOf(listOf(1,2), listOf(3,4))
val flatDoubled = nested.flatMap { it.map { it * 2 } } // [2,4,6,8]
函数时间复杂度说明
map / mapIndexedO(n)适合转换数组/列表,避免在嵌套循环里使用
flatMap / flatMapIndexedO(n)展平嵌套列表后使用 map

应用场景:数据库实体转换、展平嵌套订单、给商品价格打折等。

过滤:filter 和 take

val nums = listOf(1,2,3,4,5)
val evens = nums.filter { it % 2 == 0 }      // [2,4]
val firstThree = nums.take(3)                // [1,2,3]
val restAfterTwo = nums.drop(2)              // [3,4,5]
val underFours = nums.takeWhile { it < 4 }   // [1,2,3]
函数时间复杂度说明
filterO(n)线性遍历
take(k) / drop(k)O(k)高效切片
takeWhile / dropWhileO(n)条件不满足就停止

应用场景:筛选活跃用户、列表分页、过滤事件、跳过已处理订单等。

查找

val words = listOf("apple", "banana", "cherry")
val firstB = words.firstOrNull { it.startsWith('b') } // banana

println(words.any { it.length > 5 })  // true
println(words.all { it.length > 0 })  // true
函数时间复杂度说明
firstOrNull / findO(n)找到就停
lastOrNullO(n)从头遍历到尾
any / all / noneO(n) 最坏能提前终止

应用场景:按 ID 查找用户、检查是否有失败交易、校验输入合法性等。

去重:distinct

val numbers = listOf(1,2,2,3)
val unique = numbers.distinct() // [1,2,3]
函数时间复杂度说明
distinct()O(n)内部用 HashSet
distinctByO(n)按指定 key 去重

应用场景:标签去重、邮箱去重、商品 ID 去重等。

分组与分区:group 与 partition

val names = listOf("Alice", "Bob", "Anna")
val grouped = names.groupBy { it.first() }
// {A=[Alice, Anna], B=[Bob]}

val (even, odd) = listOf(1,2,3,4).partition { it % 2 == 0 }
// even=[2,4], odd=[1,3]

val letters = listOf('a','b','a')
val counts = letters.groupingBy { it }.eachCount() // {a=2, b=1}
函数时间复杂度说明
groupByO(n)转成 Map 列表
partitionO(n)分成两个列表
groupingBy.eachCount()O(n)高效统计频次

应用场景:商品分类、任务划分、统计频率、按角色分组用户等。

排序:sort

data class User(val name: String, val salary: Int)
val users = mutableListOf(User("John",5000), User("Alice",7000))

// 返回新的排序后的列表
val sortedUsers = users.sortedWith(
    compareByDescending<User> { it.salary }.thenBy { it.name }
)

// 原地排序
users.sortWith(
    compareByDescending<User> { it.salary }.thenBy { it.name }
)
函数时间复杂度说明
sorted / sortedBy / sortedWithO(n log n)返回新列表,稳定排序
sortWithO(n log n)原地修改

应用场景:排行榜、薪资排序、优先级队列等。

聚合与归约:fold、reduce、sumOf

val nums = listOf(1,2,3)
val sum = nums.fold(0) { acc, n -> acc + n }    // 6
val product = nums.reduce { acc, n -> acc * n } // 6
val totalSalary = users.sumOf { it.salary }     // 12000
函数时间复杂度说明
fold / reduceO(n)线性遍历累加
sumOfO(n)直接求和

应用场景:计算总收入、库存估值、指标乘积等。

键值映射:associateBy 和 zip

val mapByName = users.associateBy { it.name }
// {"John"=User("John",5000), "Alice"=User("Alice",7000)}

val scores = listOf(90, 80)
val zipped = listOf("Alice", "Bob").zip(scores) { name, score ->
    "$name scored $score"
}
// ["Alice scored 90", "Bob scored 80"]
函数时间复杂度说明
associateBy / associateWithO(n)生成 HashMap
zipO(n)两两组合

应用场景:构建查找表、生成报表、名称与数值配对等。

展平:flat

val nested = listOf(listOf(1,2), listOf(3,4))
val flat = nested.flatten() // [1,2,3,4]

val flatDoubled = nested.flatMap { it.map { it*2 } } // [2,4,6,8]
函数时间复杂度说明
flatten()O(n)n 为总元素数
flatMap()O(n)展平 + 转换

应用场景:展平订单结构、批量转换列表项等。

惰性求值:asSequence

val numbers = (1..1_000_000).asSequence()
val result = numbers.map { it * 2 }
                .filter { it % 3 == 0 }
                .take(5)
                .toList()
函数时间复杂度说明
asSequenceO(1) 创建惰性求值,只有终端操作才真正执行

应用场景:高效处理超大数据集、日志流、大型计算等。

窗口与分块:window 和 chunked

val numbers = (1..5).toList()
println(numbers.windowed(3)) // [[1,2,3],[2,3,4],[3,4,5]]
println(numbers.chunked(2))  // [[1,2],[3,4],[5]]
函数时间复杂度说明
windowed(k)O(n*k)窗口复制,k 大时注意性能
chunked(k)O(n)线性分块

应用场景:数据分析、滑动平均、批量处理等。

带索引遍历:index

val names = listOf("Alice", "Bob")
for ((index, name) in names.withIndex()) {
    println("$index -> $name")
}
函数时间复杂度说明
withIndexO(n)无额外开销
mapIndexedO(n)带索引转换

应用场景:设置位置、展示排名、显示行号等。

副作用:onEach

val doubled = (1..5).map { it*2 }
                 .onEach { println(it) }
                 .toList()
函数时间复杂度说明
onEachO(n)对每个元素执行操作

应用场景:链式调用中打印日志、调试、触发事件等。

条件返回:takeIf

val number = 10
println(number.takeIf { it % 2 == 0 })     // 10
println(number.takeUnless { it % 2 == 0 }) // null
函数时间复杂度说明
takeIf / takeUnlessO(1)仅做一次条件判断

应用场景:内联校验、可选过滤、适配链式调用。

安全访问

val arr = listOf(10, 20)
val second = arr.getOrNull(1) // 越界返回 null
函数时间复杂度说明
getOrNullO(1)避免越界异常

函数式序列:generateSequence

val fib = generateSequence(Pair(0,1)) {
    Pair(it.second, it.first + it.second)
}
    .map { it.first }
    .take(10)
    .toList()

println(fib) // [0,1,1,2,3,5,8,13,21,34]
函数时间复杂度说明
generateSequence每个元素 O(1)惰性,可生成无限序列

应用场景:斐波那契数列、惰性事件流、无限生成器等。