我有一个 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
扩展函数(保留这些类型函数的典型命名,例如 map
和 mapTo
):
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()?
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