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 的 ListMutableList 接口提供了丰富而直观的操作 API。理解其只读/可变的设计哲学,合理选择创建方式与操作方法,能有效避免并发修改、视图失效等常见问题。

文中所有示例代码均可在 GitHub 获取:Baeldung Kotlin 教程仓库

如需进一步学习 Kotlin 特性,欢迎查阅我们的 Kotlin 入门教程系列


原始标题:Working With Lists in Kotlin