当前位置:  开发笔记 > Android > 正文

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

如何解决《在Kotlin中,如何在迭代时修改列表的内容》经验,为你挑选了1个好方法。

我有一个清单:

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

我想在修改一些值时迭代它.我知道我可以这样做,map但是它会复制一份清单.

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

如果没有副本我该怎么办?

注意: 这个问题是由作者故意编写和回答的(自答案问题),因此对于常见问题的Kotlin主题的惯用答案存在于SO中.还要澄清为Kotlin的alphas写的一些非常古老的答案,这些答案对于当前的Kotlin来说是不准确的.



1> Jayson Minar..:

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

其次,要"就地"修改列表,您需要使用一种可变的列表.在您使用的示例中,listOf它返回List接口,并且是只读的.您需要直接引用可变列表的类(即ArrayList),或者是惯用的Kotlin来使用辅助函数arrayListOflinkedListOf创建MutableList引用.完成后,您可以使用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  MutableList.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,因为大多数迭代器只有一个remove()方法,而不是set().

数组的扩展函数

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

inline fun  Array.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)
        }
    }
}

关于仅使用参考平等的优化

扩展函数上述通过不设置值,如果它没有改变到一个不同的实例,检查,使用优化一点 ===!==是参考平等.它不值得检查,equals()或者hashCode()因为调用它们的成本未知,实际上参考平等会捕获任何改变值的意图.

扩展功能的单元测试

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

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)
    }
}

推荐阅读
乐韵答题
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有