1. 概述

列表是开发中最常用的数据结构之一。在处理大量数据时,就地修改列表(Modify List In-Place)往往能显著提升性能,特别是在某些操作依赖前一个元素结果的场景下。

所谓“就地修改”,指的是在遍历列表的同时直接修改其内容,而不是创建新列表。本文将介绍几种 Kotlin 中实现列表就地修改的方式。

2. 可变与不可变列表

在 Kotlin 中,列表分为可变(MutableList)和不可变(List)两种类型:

✅ 可变列表允许添加、删除和修改元素
❌ 不可变列表一旦创建就不能更改内容

由于我们要实现的是就地修改,因此本文中将使用 MutableList

3. 使用 Iterator 就地修改

Kotlin 的 MutableList 提供了 listIterator() 方法,返回一个 MutableListIterator。它除了支持 hasNext()next(),还提供了 set() 方法用于修改最后一次通过 next() 获取的元素。

示例:将列表中的偶数替换为 0:

fun replaceEvenNumbersBy0Iterator(list: MutableList<Int>): MutableList<Int> {
    val iterator = list.listIterator()
    while (iterator.hasNext()) {
        val value = iterator.next()
        if (value % 2 == 0) {
            iterator.set(0)
        }
    }
    return list
}

✅ 优点:语义清晰,适合在遍历中修改元素的场景
⚠️ 注意:set() 必须在调用 next() 后调用,否则会抛异常

4. 使用索引直接赋值修改

另一种方式是通过索引逐个访问并修改元素:

fun replaceEvenNumbersBy0Direct(list: MutableList<Int>): MutableList<Int> {
    for (i in 0 until list.size) {
        val value = list[i]
        if (value % 2 == 0) {
            list[i] = 0
        }
    }
    return list
}

✅ 简洁直观,适合大多数线性修改场景
⚠️ 注意:频繁修改大列表时注意性能

5. 创建扩展方法实现通用修改

Kotlin 支持为已有类添加扩展方法,无需继承或装饰类。我们可以为 MutableList 添加一个 mapInPlace 方法,用于就地修改所有元素。

5.1 基本实现

fun <T> MutableList<T>.mapInPlace(mutator: (T) -> T) {
    this.forEachIndexed { i, value ->
        val changedValue = mutator(value)
        this[i] = changedValue
    }
}

使用示例:

val list = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
list.mapInPlace {
    if (it % 2 == 0) 0 else it
}
// 输出:[1, 0, 3, 0, 5, 0, 7, 0, 9, 0]
println(list)

5.2 性能优化:仅当值变化时才更新

可以加入判断,避免不必要的赋值操作:

fun <T> MutableList<T>.mapInPlace(mutator: (T) -> T) {
    this.forEachIndexed { i, value ->
        val changedValue = mutator(value)
        if (value != changedValue) {
            this[i] = changedValue
        }
    }
}

5.3 扩展到 Array 类型

同样可以为数组添加类似功能:

fun <T> Array<T>.mapInPlace(mutator: (T) -> T) {
    this.forEachIndexed { i, value ->
        val changedValue = mutator(value)
        if (value != changedValue) {
            this[i] = changedValue
        }
    }
}

✅ 优点:复用性强,逻辑清晰
⚠️ 踩坑提醒:泛型中 == 比较的是值,如果是对象类型要注意重写 equals()

6. 小结

本文介绍了几种 Kotlin 中实现列表就地修改的方法:

方法 适用场景 特点
Iterator.set() 遍历中修改元素 语义明确,适合迭代器风格
索引直接赋值 简单线性修改 简洁高效
扩展方法 通用、可复用 提升代码可读性与复用性

所有示例代码已上传至 GitHub,欢迎参考。


原始标题:Modifying Kotlin Lists In-Place