1. 概述

在 Kotlin 中操作集合是开发中非常常见的任务。作为集合中最基础的类型之一,列表(List) 经常需要我们在遍历的同时动态添加新元素。

本文将介绍几种在 Kotlin 中遍历列表时添加元素的常见方式,并结合示例代码讲解不同场景下的实现方案。

2. 问题引入

为了更直观地理解问题,我们来看一个例子:

假设我们有如下字符串列表:

["ab", "a", "cd", "c", "xyz"]

现在我们希望 **在遍历过程中,每当遇到长度大于 1 的字符串时,就在其后插入一个标记字符串 <- a long one**,最终结果如下:

["ab", "<- a long one", "a", "cd", "<- a long one", "c", "xyz", "<- a long one"]

或者,**根据需求不同,我们可能需要在这些“长字符串”前插入 a long one ->**:

["a long one ->", "ab", "a", "a long one ->", "cd", "c", "a long one ->", "xyz"]

Kotlin 中有只读列表和可变列表之分,因此我们需根据列表类型和插入位置的不同分别处理。

3. 当列表是只读的(Read-only)

如果列表是只读的:

val myList = listOf("ab", "a", "cd", "c", "xyz")

那么我们无法直接修改该列表。此时,我们通常的做法是创建一个可变列表,遍历原列表并按需添加元素,最后返回一个新列表。

3.1 使用 forEach()

我们可以使用 forEach() 遍历原列表,并将符合条件的元素插入到一个新的 mutableListOf() 中:

fun byForEach(list: List<String>): List<String> {
    val result = mutableListOf<String>()
    list.forEach {
        result += it
        if (it.length > 1) {
            result += "<- a long one"
        }
    }
    return result.toList()
}

优点:逻辑清晰,适合初学者理解。

缺点:需要手动创建中间列表,代码略显冗长。

测试:

assertEquals(
    listOf("ab", "<- a long one", "a", "cd", "<- a long one", "c", "xyz", "<- a long one"),
    byForEach(myList)
)

3.2 使用 buildList()

Kotlin 提供了 buildList() 函数,可以更简洁地构建列表:

fun byBuildList(list: List<String>) =
    buildList {
        list.forEach {
            this += it
            if (it.length > 1) {
                this += "<- a long one"
            }
        }
    }

优点:语法简洁,使用 += 操作符即可添加元素。

内部使用可变列表,最终返回只读列表。

测试:

assertEquals(
    listOf("ab", "<- a long one", "a", "cd", "<- a long one", "c", "xyz", "<- a long one"),
    byBuildList(myList)
)

3.3 在元素前插入内容

如果我们希望在“长字符串”前面插入内容,只需调整插入顺序即可。

buildList() 为例:

fun addBeforeByBuildList(list: List<String>) =
    buildList {
        list.forEach {
            if (it.length > 1) {
                this += "a long one ->"
            }
            this += it
        }
    }

测试:

assertEquals(
    listOf("a long one ->", "ab", "a", "a long one ->", "cd", "c", "a long one ->", "xyz"),
    addBeforeByBuildList(myList)
)

4. 当列表是可变的(Mutable)

如果列表是可变的:

val myMutableList = mutableListOf("ab", "a", "cd", "c", "xyz")

我们无需创建新列表,可以直接在遍历过程中修改原列表。

4.1 使用 ListIterator

使用 ListIterator 可以在遍历的同时修改列表内容,比如插入或删除元素:

fun byListIterator(list: MutableList<String>) {
    val it = list.listIterator()
    for (e in it) {
        if (e.length > 1) {
            it.add("<- a long one")
        }
    }
}

优点:无需额外内存,直接修改原列表。

⚠️ 注意:不能使用普通 for 循环或 forEach 直接修改列表,否则会抛出 ConcurrentModificationException

测试:

byListIterator(myMutableList)
assertEquals(
    listOf("ab", "<- a long one", "a", "cd", "<- a long one", "c", "xyz", "<- a long one"),
    myMutableList
)

4.2 在元素前插入内容

如果我们希望在“长字符串”前面插入内容,可以使用 ListIterator.previous()next() 配合完成:

fun addBeforeByListIterator(list: MutableList<String>) {
    val it = list.listIterator()
    for (e in it) {
        if (e.length > 1) {
            it.previous()
            it.add("a long one ->")
            it.next()
        }
    }
}

通过 previous() 回退指针,插入新元素,再通过 next() 恢复遍历位置。

测试:

val myMutableList = mutableListOf("ab", "a", "cd", "c", "xyz")
addBeforeByListIterator(myMutableList)
assertEquals(
    listOf("a long one ->", "ab", "a", "a long one ->", "cd", "c", "a long one ->", "xyz"),
    myMutableList
)

5. 总结

在 Kotlin 中,我们可以通过多种方式在遍历列表时动态添加元素:

场景 方法 特点
只读列表 forEach() + mutableListOf() 简单易懂,适合新手
只读列表 buildList() 语法简洁,推荐使用
可变列表 ListIterator 原地修改,节省内存
插入位置 调整插入顺序 前插需配合 previous()next()

⚠️ 注意:在使用可变列表时,不能直接使用普通循环修改原列表,否则会触发并发修改异常。

完整示例代码可参考 GitHub 项目地址


原始标题:Iterate Through a List and Add Items in Kotlin