第8章:集合框架——List、Set、Map的创建与操作

说实话,Kotlin的集合框架是我最喜欢的一部分。为什么?因为它把「可变」和「不可变」分得清清楚楚,这在Java里可没有这么爽。我记得刚转Kotlin那会儿,写Java写习惯了,总想着用new ArrayList(),结果发现Kotlin里直接listOf()就搞定了。嗯,今天我们就来好好聊聊集合框架。

8.1 集合的三大金刚:List、Set、Map

说白了,集合就是用来装数据的容器。Kotlin提供了三种核心集合类型:

  • List:有序的集合,可以重复。就像排队买奶茶,每个人都有一个位置。
  • Set:无序的集合,不能重复。就像你的身份证号,全世界只有一个。
  • Map:键值对的集合,键唯一。就像查字典,一个词对应一个解释。

我个人习惯把这三者记成「排队、去重、查表」,好记又形象。

8.2 创建集合:从零开始

Kotlin提供了非常简洁的创建方式。你想想看,Java里要写new ArrayList<String>(),Kotlin里只需要:

// 创建不可变集合
val list = listOf("苹果", "香蕉", "橘子")
val set = setOf(1, 2, 3, 3, 3)  // 结果只有1,2,3
val map = mapOf("key1" to "value1", "key2" to "value2")

// 创建可变集合
val mutableList = mutableListOf("苹果", "香蕉")
val mutableSet = mutableSetOf(1, 2, 3)
val mutableMap = mutableMapOf("key1" to "value1")

我在项目中遇到过一个问题:新手容易把listOfmutableListOf搞混。记住一个原则——能用不可变就用不可变,只有确定要修改时才用可变。这能避免很多莫名其妙的bug。

💡 我的小技巧: 如果你不确定集合会不会变,先用listOf。编译器会提醒你,如果后面试图修改它,那就改成mutableListOf。这叫「最小可变性原则」。

8.3 可变 vs 不可变:一个重要的设计哲学

Kotlin把可变和不可变分成了两套接口。比如List是只读的,MutableList才是可写的。为什么要这么设计?

我曾经在一个大型项目中,因为一个集合被多处代码修改,导致数据错乱,查了两天才找到原因。从那以后,我强制团队:所有函数参数都用不可变集合,除非明确需要修改。

集合类型 不可变 可变
List listOf() mutableListOf()
Set setOf() mutableSetOf()
Map mapOf() mutableMapOf()
⚠️ 避坑指南: 我曾经把一个MutableList传给一个第三方库,结果那个库偷偷往里面加了数据,导致我的UI刷新异常。后来我改成list.toList()传过去,问题就解决了。记住:传出去的数据,最好给不可变副本。

8.4 集合的常用操作:增删改查

可变集合的操作很直观。我直接上代码:

val list = mutableListOf("A", "B", "C")

// 增
list.add("D")
list.addAll(listOf("E", "F"))

// 删
list.remove("A")
list.removeAt(0)
list.clear()  // 全部清空

// 改
list[0] = "Z"

// 查
val first = list.first()
val last = list.last()
val hasB = "B" in list  // true or false

你可能会问:为什么listOf创建的集合不能add?因为它的底层实现就没有写方法。Kotlin在编译时就帮你堵住了这个漏洞,而不是等到运行时才报错。这一点比Java强太多了。

8.5 集合的扩展函数:Kotlin的杀手锏

Kotlin给集合加了一大堆扩展函数,用起来特别爽。我挑几个最常用的:

val numbers = listOf(1, 2, 3, 4, 5, 6)

// 过滤
val evens = numbers.filter { it % 2 == 0 }  // [2, 4, 6]

// 映射
val doubled = numbers.map { it * 2 }  // [2, 4, 6, 8, 10, 12]

// 分组
val grouped = numbers.groupBy { if (it % 2 == 0) "偶数" else "奇数" }
// 结果:{"奇数"=[1,3,5], "偶数"=[2,4,6]}

// 排序
val sorted = numbers.sortedDescending()  // [6,5,4,3,2,1]

// 去重
val withDuplicates = listOf(1, 1, 2, 2, 3)
val unique = withDuplicates.distinct()  // [1,2,3]

// 取前N个
val firstThree = numbers.take(3)  // [1,2,3]

我个人最常用的是filtermap的组合。比如从用户列表中找出所有活跃用户的名字:

data class User(val name: String, val isActive: Boolean)

val users = listOf(
    User("张三", true),
    User("李四", false),
    User("王五", true)
)

val activeNames = users
    .filter { it.isActive }
    .map { it.name }
// 结果:["张三", "王五"]

这种链式调用,读起来就像在说「从用户里过滤出活跃的,然后取出名字」。代码即文档,说的就是这种感觉。

8.6 集合的知识体系

下面这张图,是我梳理的集合框架核心脉络。你看一眼就能明白整体结构:

Kotlin 集合框架核心结构 Kotlin 集合框架 List Set Map listOf() mutableListOf() setOf() mutableSetOf() mapOf() mutableMapOf() 常用扩展函数 filter map groupBy sorted distinct take forEach reduce fold any all none 不可变集合 → 线程安全,推荐优先使用

8.7 实战中的选择建议

说了这么多,到底什么时候用哪个?我总结了几条经验:

  • 数据展示用List:比如列表页、表格数据,有序且可能需要重复。
  • 去重场景用Set:比如标签集合、ID集合,保证唯一性。
  • 键值查询用Map:比如配置项、缓存数据,通过键快速取值。
  • 函数参数用不可变:避免被意外修改,这是我在代码review时反复强调的。
  • 链式操作多用扩展函数:一行代码搞定过滤+映射+排序,比写for循环优雅多了。

📌 核心要点回顾:

  • Kotlin把集合分为可变和不可变,这是设计上的进步
  • 创建集合用xxxOf()系列函数,简洁明了
  • 扩展函数是Kotlin集合的灵魂,filtermapgroupBy是高频操作
  • 优先使用不可变集合,只在需要修改时用可变集合

好了,集合框架的内容就聊到这儿。你只要把listOfsetOfmapOf这三个创建方式记住,再把filtermap练熟,日常开发基本就够用了。剩下的扩展函数,用到的时候查一下文档,慢慢就都记住了。


公众号:蓝海资料掘金营,微信deep3321