
HR:你有多少年 Kotlin 使用经验?
候选人:5 年!
双方都挺满意。但你真的觉得,做了五年 Android 开发,就把 Kotlin 用透吗?或许你只用到了它的 20%。
我深入研究 Kotlin 时才发现,我就是个"伪 Kotlin 开发者"------它有太多强大的方法、扩展函数,还有很多我从没接触过的特性。
做算法题时,一看到数组,脑子里第一反应就是写个 for 循环。
但如果你用 Kotlin 解决问题,其实有很多更优雅的替代方案。
这篇文章,我会把 Kotlin 集合的惯用写法过一遍,它们会彻底改变你的代码风格。
Kotlin 标准库提供了极其丰富的集合操作,让你能写出简洁、可读、安全且高效 的代码。使用地道的 Kotlin 写法,可以帮你避免样板循环、手动映射、计数器变量等冗余代码。
转换:map 和 flatMap
作用:对集合里的每一个元素进行转换。
kotlin
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 / mapIndexed |
O(n) | 适合转换数组/列表,避免在嵌套循环里使用 |
flatMap / flatMapIndexed |
O(n) | 展平嵌套列表后使用 map |
应用场景:数据库实体转换、展平嵌套订单、给商品价格打折等。
过滤:filter 和 take
kotlin
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]
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
filter |
O(n) | 线性遍历 |
take(k) / drop(k) |
O(k) | 高效切片 |
takeWhile / dropWhile |
O(n) | 条件不满足就停止 |
应用场景:筛选活跃用户、列表分页、过滤事件、跳过已处理订单等。
查找
kotlin
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 / find |
O(n) | 找到就停 |
lastOrNull |
O(n) | 从头遍历到尾 |
any / all / none |
O(n) 最坏 | 能提前终止 |
应用场景:按 ID 查找用户、检查是否有失败交易、校验输入合法性等。
去重:distinct
kotlin
val numbers = listOf(1,2,2,3)
val unique = numbers.distinct() // [1,2,3]
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
distinct() |
O(n) | 内部用 HashSet |
distinctBy |
O(n) | 按指定 key 去重 |
应用场景:标签去重、邮箱去重、商品 ID 去重等。
分组与分区:group 与 partition
kotlin
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}
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
groupBy |
O(n) | 转成 Map 列表 |
partition |
O(n) | 分成两个列表 |
groupingBy.eachCount() |
O(n) | 高效统计频次 |
应用场景:商品分类、任务划分、统计频率、按角色分组用户等。
排序:sort
kotlin
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 / sortedWith |
O(n log n) | 返回新列表,稳定排序 |
sortWith |
O(n log n) | 原地修改 |
应用场景:排行榜、薪资排序、优先级队列等。
聚合与归约:fold、reduce、sumOf
kotlin
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 / reduce |
O(n) | 线性遍历累加 |
sumOf |
O(n) | 直接求和 |
应用场景:计算总收入、库存估值、指标乘积等。
键值映射:associateBy 和 zip
kotlin
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 / associateWith |
O(n) | 生成 HashMap |
zip |
O(n) | 两两组合 |
应用场景:构建查找表、生成报表、名称与数值配对等。
展平:flat
kotlin
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
kotlin
val numbers = (1..1_000_000).asSequence()
val result = numbers.map { it * 2 }
.filter { it % 3 == 0 }
.take(5)
.toList()
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
asSequence |
O(1) 创建 | 惰性求值,只有终端操作才真正执行 |
应用场景:高效处理超大数据集、日志流、大型计算等。
窗口与分块:window 和 chunked
kotlin
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
kotlin
val names = listOf("Alice", "Bob")
for ((index, name) in names.withIndex()) {
println("$index -> $name")
}
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
withIndex |
O(n) | 无额外开销 |
mapIndexed |
O(n) | 带索引转换 |
应用场景:设置位置、展示排名、显示行号等。
副作用:onEach
kotlin
val doubled = (1..5).map { it*2 }
.onEach { println(it) }
.toList()
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
onEach |
O(n) | 对每个元素执行操作 |
应用场景:链式调用中打印日志、调试、触发事件等。
条件返回:takeIf
kotlin
val number = 10
println(number.takeIf { it % 2 == 0 }) // 10
println(number.takeUnless { it % 2 == 0 }) // null
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
takeIf / takeUnless |
O(1) | 仅做一次条件判断 |
应用场景:内联校验、可选过滤、适配链式调用。
安全访问
kotlin
val arr = listOf(10, 20)
val second = arr.getOrNull(1) // 越界返回 null
| 函数 | 时间复杂度 | 说明 |
|---|---|---|
getOrNull |
O(1) | 避免越界异常 |
函数式序列:generateSequence
kotlin
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) | 惰性,可生成无限序列 |
应用场景:斐波那契数列、惰性事件流、无限生成器等。