list - 在 Kotlin 中,如何在迭代时修改列表的内容

我有一个 list :

val someList = listOf(1, 20, 10, 55, 30, 22, 11, 0, 99)

我想在修改一些值的同时对其进行迭代。我知道我可以使用 map 做到这一点,但这会复制列表。

val copyOfList = someList.map { if (it <= 20) it + 20 else it }

没有副本我该怎么做?

注意: 这个问题是作者有意编写和回答的 (Self-Answered Questions),因此常见的 Kotlin 主题的惯用答案出现在 SO 中。还要澄清一些为 Kotlin alpha 编写的非常古老的答案,这些答案对于当前的 Kotlin 并不准确。

最佳答案

首先,并非所有复制列表都是不好的。有时副本可以利用 CPU 缓存并且速度非常快,这取决于列表、大小和其他因素。

其次,要“就地”修改列表,您需要使用可变的列表类型。在您的示例中,您使用 listOf返回 List<T>接口(interface),并且是只读的。您需要直接引用可变列表的类(即 ArrayList ),或者使用帮助函数 arrayListOf 是惯用的 Kotlin或 linkedListOf创建 MutableList<T>引用。一旦你有了它,你可以使用 listIterator() 来迭代列表。它有一个突变方法set() .

// create a mutable list
val someList = arrayListOf(1, 20, 10, 55, 30, 22, 11, 0, 99)

// iterate it using a mutable iterator and modify values 
val iterate = someList.listIterator()
while (iterate.hasNext()) {
    val oldValue = iterate.next()
    if (oldValue <= 20) iterate.set(oldValue + 20)
}

这将在迭代发生时更改列表中的值,并且对所有列表类型都有效。为了使这更容易,请创建可以重复使用的有用的扩展函数(见下文)。

使用简单的扩展函数进行变异:

您可以为 Kotlin 编写扩展函数,对任何 MutableList 进行就地可变迭代。执行。这些内联函数的执行速度与迭代器的任何自定义使用一样快,并且内联以提高性能。非常适合 Android 或任何地方。

这里是 mapInPlace扩展函数(保留这些类型函数的典型命名,例如 mapmapTo ):

inline fun <T> MutableList<T>.mapInPlace(mutator: (T)->T) {
    val iterate = this.listIterator()
    while (iterate.hasNext()) {
        val oldValue = iterate.next()
        val newValue = mutator(oldValue)
        if (newValue !== oldValue) {
            iterate.set(newValue)
        }
    }
}

示例调用此扩展函数的任何变体:

val someList = arrayListOf(1, 20, 10, 55, 30, 22, 11, 0, 99)
someList.mapInPlace { if (it <= 20) it + 20 else it }

这并不适用于所有 Collection<T> , 因为大多数迭代器只有 remove()方法,而不是 set() .

数组的扩展函数

您可以使用类似的方法处理泛型数组:

inline fun <T> Array<T>.mapInPlace(mutator: (T)->T) {
    this.forEachIndexed { idx, value ->
        mutator(value).let { newValue ->
            if (newValue !== value) this[idx] = mutator(value)
        }
    }
}

对于每个原始数组,使用以下变体:

inline fun BooleanArray.mapInPlace(mutator: (Boolean)->Boolean) {
    this.forEachIndexed { idx, value ->
        mutator(value).let { newValue ->
            if (newValue !== value) this[idx] = mutator(value)
        }
    }
}

关于仅使用引用相等的优化

上面的扩展函数通过不设置值来优化一点,如果它没有改变到不同的实例,检查使用 ===!==是 Referential Equality .不值得检查 equals()hashCode()因为调用它们有一个未知的成本,并且实际上引用相等捕获了任何改变值的意图。

扩展函数的单元测试

这里是显示函数工作的单元测试用例,以及与 stdlib 函数 map() 的小比较复制:

class MapInPlaceTests {
    @Test fun testMutationIterationOfList() {
        val unhappy = setOf("Sad", "Angry")
        val startingList = listOf("Happy", "Sad", "Angry", "Love")
        val expectedResults = listOf("Happy", "Love", "Love", "Love")

        // modify existing list with custom extension function
        val mutableList = startingList.toArrayList()
        mutableList.mapInPlace { if (it in unhappy) "Love" else it }
        assertEquals(expectedResults, mutableList)
    }

    @Test fun testMutationIterationOfArrays() {
        val otherArray = arrayOf(true, false, false, false, true)
        otherArray.mapInPlace { true }
        assertEquals(arrayOf(true, true, true, true, true).toList(), otherArray.toList())
    }

    @Test fun testMutationIterationOfPrimitiveArrays() {
        val primArray = booleanArrayOf(true, false, false, false, true)
        primArray.mapInPlace { true }
        assertEquals(booleanArrayOf(true, true, true, true, true).toList(), primArray.toList())
    }

    @Test fun testMutationIterationOfListWithPrimitives() {
        val otherList = arrayListOf(true, false, false, false, true)
        otherList.mapInPlace { true }
        assertEquals(listOf(true, true, true, true, true), otherList)
    }
}

https://stackoverflow.com/questions/34608551/

相关文章:

kotlin - 为什么 UInt 没有 toDouble()?

string - Kotlin 多行字符串中的模板转义

gradle - 如何使用 kotlinscript DSL (build.gradle.kts)

kotlin - 字符串插值在 Kotlin 中是如何工作的?

spring - Kotlin 和 Spring Boot @ConfigurationProper

android - RealmObject 的 Kotlin 数据类

variables - 在 Kotlin 中一次定义多个变量(例如 Java : String x,

android - 警告 "Kotlin plugin version is not the sam

constructor - Kotlin 数据类可以有多个构造函数吗?

android - 更新到 Kotlin 1.3.30 会破坏 Dagger 2.21 的构建