1. 概述
本文将深入探讨 Kotlin 中列表(List)的使用方式,涵盖创建、遍历、元素检索、修改、排序等核心操作。作为 Kotlin 集合体系中最常用的类型之一,掌握 List 的正确用法能极大提升开发效率,避免一些常见的“踩坑”场景。
2. Kotlin 中的 List 接口
在 Kotlin 中,List
是一个泛型的有序集合接口,继承自 Collection<E>
,✅ 允许存储重复元素。
public interface List<out E> : Collection<E>
⚠️ 关键点:
List
接口本身是只读的,所有方法仅支持访问和查询,不支持增删改。- 若需修改列表内容,应使用其子接口
MutableList
。
MutableList
扩展了 MutableCollection
,提供了添加、删除、替换等写操作能力:
public interface MutableList<E> : List<E>, MutableCollection<E>
这两个接口构成了 Kotlin 列表操作的基础,实际开发中需根据是否需要修改来选择使用哪一个。
3. 创建列表
创建只读列表
使用 listOf()
创建不可变列表:
val countries = listOf("Germany", "India", "Japan", "Brazil", "Australia")
该列表一旦创建,无法通过任何方式修改其内容 ❌。
创建可变列表
使用 mutableListOf()
创建可变列表:
val cities = mutableListOf("Berlin", "Calcutta", "Seoul", "Sao Paulo", "Sydney")
此列表支持后续的增删改操作 ✅。
💡 建议:除非明确需要修改,否则优先使用
listOf()
,有助于写出更安全、线程友好的代码。
4. 遍历列表
4.1 使用循环与高阶函数
for 循环遍历元素
for (country in countries) {
country.length
// ...
}
使用索引遍历
for (i in 0 until countries.size) {
countries[i].length
// ...
}
forEach 高阶函数
推荐使用函数式风格,代码更简洁:
countries.forEach { it ->
it.length
// ...
}
forEachIndexed 获取索引
当需要同时获取索引和元素时:
countries.forEachIndexed { i, e ->
e.length
// ...
}
4.2 ListIterator(双向迭代)
适用于需要前后移动的场景,例如逆序处理或从指定位置开始:
fun iterateUsingListIterator() {
val iterator = countries.listIterator()
while (iterator.hasNext()) {
val country = iterator.next()
// 正向遍历
}
while (iterator.hasPrevious()) {
val country = iterator.previousIndex();
// 可反向遍历
}
}
nextIndex()
和 previousIndex()
可动态获取当前位置信息。
4.3 MutableListIterator(可变迭代器)
在遍历过程中允许修改列表结构,这是普通 Iterator
不支持的:
fun iterateUsingMutableListIterator() {
val iterator = cities.listIterator(1)
iterator.next()
iterator.add("London") // 插入新元素
iterator.next()
iterator.set("Milan") // 替换当前元素
// ...
}
⚠️ 踩坑提醒:在使用 iterator.remove()
或 listIterator.add()
时,确保未对原集合进行并发修改,否则可能抛出 ConcurrentModificationException
。
5. 获取列表元素
通过索引访问
推荐使用数组风格的 []
操作符,语法更简洁:
val element = countries[2] // 推荐
val element = countries.get(3) // 等价但略显冗长
获取首尾元素
countries.first()
countries.last()
支持带条件的查找:
countries.first { it.length > 7 } // 第一个长度大于7的国家
countries.last { it.startsWith("J") } // 最后一个以J开头的国家
countries.firstOrNull { it.length > 8 } // 安全查找,无匹配返回 null
✅ 强烈建议使用 firstOrNull()
替代 first()
,避免因空集合或无匹配项导致 NoSuchElementException
。
6. 截取列表片段
subList:返回视图(View)
val subList = countries.subList(1, 4) // 包含索引1,不包含4
⚠️ 注意:subList()
返回的是原列表的一个视图,不是新列表。对原列表的结构性修改可能导致 subList
行为未定义(undefined behavior),极易引发运行时异常。
slice:生成新列表
更安全的选择是使用 slice()
,它会创建一个新的独立列表:
val sliceListUsingIndices = countries.slice(1..4) // 使用区间
assertEquals(4, sliceListUsingIndices.size)
val sliceListUsingCollection = countries.slice(listOf(1, 4)) // 使用索引列表
assertEquals(2, sliceListUsingCollection.size)
✅ 推荐:除非明确需要共享底层数据,否则优先使用 slice()
。
7. 统计元素数量
val count = countries.count() // 总数
val filteredCount = countries.count { it.length > 5 } // 条件计数
val size = countries.size // 属性方式获取大小
size
是属性,性能最优。count()
支持传入谓词函数,适合条件统计。
8. 写操作(仅限 MutableList)
添加元素
cities.add("Barcelona") // 尾部添加
cities.add(3, "London") // 指定位置插入
cities.addAll(listOf("Singapore", "Moscow")) // 批量添加
cities.addAll(2, listOf("Prague", "Amsterdam")) // 指定位置批量插入
删除元素
cities.remove("Seoul") // 按值删除
cities.removeAt(1) // 按索引删除
// cities.removeAll(listOf("X", "Y")) // 批量删除
修改元素
cities.set(3, "Prague") // set 方法
cities[4] = "Moscow" // 索引赋值,更简洁
cities.fill("Barcelona") // 所有元素替换为同一值
9. 排序操作
原地排序(仅限 MutableList)
cities.sort() // 升序
cities.sortDescending() // 降序
⚠️ 这些方法直接修改原列表,且要求列表为可变类型。
返回新列表(推荐用于不可变列表)
val sortedCountriesAscending = countries.sorted()
val sortedCountriesDescending = countries.sortedDescending()
按属性排序
// 假设对象列表,按 name 字段排序
users.sortBy { it.name }
users.sortByDescending { it.age }
// 返回新列表
val sortedUsers = users.sortedBy { it.createdAt }
自定义比较器
cities.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it })
📌 更多排序技巧可参考我们的 Kotlin 排序指南。
10. 检查元素是否存在
assertTrue(countries.contains("Germany"))
assertFalse("Spain" in countries) // 推荐使用 'in' 操作符,更 Kotlin 风格
assertTrue(cities.containsAll(listOf("Calcutta", "Sao Paulo", "Sydney")))
contains()
和in
功能相同,但in
更符合 Kotlin 语言习惯。containsAll()
判断多个元素是否全部存在。
11. 总结
Kotlin 的 List
与 MutableList
接口提供了丰富而直观的操作 API。理解其只读/可变的设计哲学,合理选择创建方式与操作方法,能有效避免并发修改、视图失效等常见问题。
文中所有示例代码均可在 GitHub 获取:Baeldung Kotlin 教程仓库。
如需进一步学习 Kotlin 特性,欢迎查阅我们的 Kotlin 入门教程系列。