一、集合家族总览
在 Kotlin 中,集合是最常用的数据结构之一,主要分为三类:
| 类型 | 用途 | 是否有序 | 是否允许重复 | 常见实现 |
|---|---|---|---|---|
| List | 有序队列 | ✅ | ✅ | ArrayList, MutableList |
| Set | 去重集合 | ⚠️(看实现) | ❌ | HashSet, LinkedHashSet |
| Map | 键值对映射表 | ⚠️(看实现) | key❌ value✅ | HashMap, LinkedHashMap |
可变 vs 只读
-
List,Set,Map→ 只读视图(不能增删改) -
MutableList,MutableSet,MutableMap→ 可变集合
二、各集合的核心特征
1. List / MutableList
👉 有序可重复,像"排队单子"。
Kotlin
val nums = mutableListOf(1, 1, 2)
nums.add(3)
nums.remove(1) // 删除第一个 1
nums.removeAll { it == 1 } // 删除所有 1
println(nums) // [2, 3]
常用实现:
-
ArrayList()→ 最常见 -
CopyOnWriteArrayList()→ 多线程读多写少
2. Set / MutableSet
👉 自动去重。无论你加几次同样元素,只留一份。
Kotlin
val set = mutableSetOf(1, 1, 2)
println(set) // [1, 2]
常见实现:
-
HashSet→ 无序,高效 -
LinkedHashSet→ 按插入顺序保存 -
TreeSet→ 自动排序(需要可比较类型)
3. Map / MutableMap
👉 键值对结构(key → value),key 唯一,后写会覆盖前写。
Kotlin
val map = mutableMapOf<Int, String>()
map[1] = "A"
map[1] = "B" // 覆盖
println(map) // {1=B}
常见实现:
-
HashMap:高效、无序 -
LinkedHashMap:保序 -
ConcurrentHashMap:多线程安全
三、关键集合实现对比表
| 类型 | 是否有序 | 是否去重 | 是否可变 | 说明 |
|---|---|---|---|---|
listOf() |
✅ | ❌ | ❌ | 只读列表 |
mutableListOf() |
✅ | ❌ | ✅ | 可增删改 |
setOf() |
⚠️ | ✅ | ❌ | 只读去重集合 |
mutableSetOf() |
⚠️ | ✅ | ✅ | 可增删改的去重集合 |
mapOf() |
⚠️ | ✅(key) | ❌ | 只读映射表 |
mutableMapOf() |
⚠️ | ✅(key) | ✅ | 可增删改映射表 |
HashMap |
❌ | ✅(key) | ✅ | 无序高效 |
LinkedHashMap |
✅ | ✅(key) | ✅ | 有序可改 |
四、实战:事件队列模型(真实场景)
场景:机器人上报"事件",同一设备可能重复上报。
Kotlin
data class DeviceRoute(
val targetId: String,
val command: String
)
// 允许重复、保序
private val targetIdList = mutableListOf<DeviceRoute>()
fun addEvent(id: String, cmd: String) {
targetIdList.add(DeviceRoute(id, cmd))
}
/** 按 command 获取全部 id(允许重复) */
fun getTargetIds(command: String): List<String> {
return targetIdList.asSequence() // ← 关键点在这里
.filter { it.command == command }
.map { it.targetId }
.toList()
}
/** 删除一个事件(只删第一个匹配项) */
fun removeOne(id: String, cmd: String): Boolean {
return targetIdList.remove(DeviceRoute(id, cmd))
}
/** 删除所有匹配事件 */
fun removeAll(id: String, cmd: String): Boolean {
return targetIdList.removeAll { it.targetId == id && it.command == cmd }
}
👉 用 List 因为要保留所有事件(包括重复),
👉 Set 会去重,不适合事件流;
👉 Map key 唯一,也会覆盖旧数据。
五、重点:asSequence() 到底干嘛?
很多人一开始看到这句:
Kotlin
targetIdList.asSequence()
.filter { it.command == command }
.map { it.targetId }
.toList()
会疑惑:"asSequence() 有啥用?"
✅ 1. 定义
asSequence() 会把集合转为一个 惰性(lazy)序列 ,
它不会立刻执行 filter/map,而是等到"终止操作"再逐个计算。
✅ 2. 原理对比
不用 asSequence:
Kotlin
val result = list
.filter { it > 10 } // 生成一个新 List
.map { it * 2 } // 再生成一个新 List
.toList() // 最后又生成一个 List
会构建多个中间集合,占内存、效率低。
用 asSequence:
Kotlin
val result = list.asSequence()
.filter { it > 10 } // 惰性:不马上创建集合
.map { it * 2 } // 按需逐个处理
.toList() // 到这里才一次性收集结果
只遍历一次列表,更高效,尤其对大数据集合。
✅ 3. 终止操作(trigger 执行)
只有当调用以下之一时才真正执行:
-
收集:
toList(),toSet(),toCollection(...) -
查找:
first(),firstOrNull(),any(),count() -
遍历:
forEach,onEach { }.toList()
例如:
Kotlin
val res = list.asSequence()
.filter { it > 10 }
.map { it * 2 }
.forEach { println(it) } // 到这里才真正遍历
✅ 4. 优点
| 优点 | 说明 |
|---|---|
| 🚀 惰性执行 | 不创建中间集合,效率更高 |
| 🧠 可读性强 | 链式写法直观 |
| 💾 节省内存 | 对大数据集合友好 |
| 🔄 保留顺序 | 不会乱序执行 |
| ✅ 与集合方法兼容 | 可随时 .asSequence() / .toList() 切换 |
✅ 5. 注意事项
-
Sequence不是并行流(不像 Java Stream); -
如果数据量小,普通集合操作和 Sequence 性能几乎一样;
-
大量链式计算时
Sequence更合适。
六、不同结构的典型使用建议
| 场景 | 推荐结构 | 说明 |
|---|---|---|
| 有序、可重复的事件 | MutableList |
保留所有事件 |
| 唯一元素集合 | MutableSet / LinkedHashSet |
去重场景 |
| 键值查表 | HashMap / LinkedHashMap |
查找快 |
| 数据多、要链式过滤 | asSequence() |
避免中间集合 |
| 多线程访问 | CopyOnWriteArrayList / ConcurrentHashMap |
安全 |
七、结语:如何选择集合?
| 你的需求 | 推荐 |
|---|---|
| 要顺序 + 重复 | MutableList |
| 要顺序 + 不重复 | LinkedHashSet |
| 不关心顺序 + 不重复 | HashSet |
| 要查表(key→value) | HashMap / LinkedHashMap |
| 想延迟计算、优化性能 | .asSequence() |
✅ "List 保序可重复,Set 去重,Map 查表,Sequence 优化链路。"
🎯 最后一句话总结
List是队列,Set是名单,Map是字典,asSequence()是让它们"边走边算"的聪明处理器。