如何在Kotlin中克隆或复制一个列表

234

如何在 Kotlin 中复制列表?

我正在使用

val selectedSeries = mutableListOf<String>()
selectedSeries.addAll(series)

有没有更简单的方法?


1
我认为你的解决方案已经是最简单的方法了,如果你不需要深度克隆的话。 - Serdar Samancıoğlu
复制列表只会复制项目的引用,而不会克隆项目本身。如果您希望拥有深度克隆的列表,请在复制列表时不要忘记克隆项目。 - CoolMind
15个回答

282

这个很好用。

val selectedSeries = series.toMutableList()

14
val selectedSeries = series.toList() 也可以工作,因为它在实现中调用了toMutableList() - Flávio Faria
8
@FlávioFaria 刚刚使用===测试了一下,我不得不说 toList() 不会复制集合,而 toMutableList() 会。 - Peppermint Paddy
5
它确实复制,但在空列表的情况下除外。如果源为空,则 Iterable.toList() 返回 emptyList(),它始终返回相同的(不可变)对象。因此,如果您使用 emptyList() 进行测试,您将获得相同的对象。 翻译后: - Laurence Gonsalves
9
这不是一个好的答案,肯定不是正确的答案。未来的实现可能会发生改变,并没有保证,除非有明确的文档说明这个方法调用始终会返回一个新的副本。 - Bhargav
11
@BrunoJCM,情况已经不同了。Kotlin文档指出toMutableList()返回一个新的列表,“返回一个由此集合中所有元素填充的新的MutableList”。 - Pär Nils Amsen
显示剩余3条评论

55

您可以使用

List -> toList()

Array -> toArray()

ArrayList -> toArray()

MutableList -> toMutableList()


示例:

val array = arrayListOf("1", "2", "3", "4")

val arrayCopy = array.toArray() // copy array to other array

Log.i("---> array " ,  array?.count().toString())
Log.i("---> arrayCopy " ,  arrayCopy?.count().toString())

array.removeAt(0) // remove first item in array 

Log.i("---> array after remove" ,  array?.count().toString())
Log.i("---> arrayCopy after remove" ,  arrayCopy?.count().toString())

打印日志:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4

你把Array和ArrayList搞混了。它们是两个完全不同的东西。在你的代码中,array实际上是一个ArrayList,而arrayCopy是一个Array。它们的命名是误导性的。 - k314159

48

如果您的列表中包含 Kotlin 数据类,您可以这样做。

selectedSeries = ArrayList(series.map { it.copy() })

如果您只想将ArrayList的一个属性复制到另一个ArrayList中,该怎么办? - Ahmad Shahwaiz
1
最佳响应。谢谢。其他的都不起作用。 - Userlambda
如果你想要一个可变的列表,你可以使用 selectedSeries = series.map { it.copy() }.toMutableList() - Samuel
1
非常感谢。当使用自定义模型或数据类的列表时,这是正确的答案。 - kodartcha
这对我来说是最好的答案,因为我们正在使用委托适配器RecyclerView。 - rizkidzulkarnain
这给了我一些处理我的情况的想法,谢谢! - pqtuan86

22

我可以提供两种替代方法:

1. val selectedSeries = mutableListOf<String>().apply { addAll(series) }

2. val selectedSeries = mutableListOf(*series.toTypedArray())

更新:随着 Kotlin 1.3 中新的类型推断引擎(可选择加入),我们可以省略第一个示例中的泛型类型参数,从而得到:

val list = listOf("a", "b", "c")
val first = list.first()
1. val selectedSeries = mutableListOf().apply { addAll(series) }

提醒一下,启用新推理的方法是使用命令行:kotlinc -Xnew-inference ./SourceCode.kt 或者在Gradle中使用 kotlin { experimental { newInference 'enable'}。如果想了解更多关于新类型推断的信息,请查看这个视频:KotlinConf 2018 - New Type Inference and Related Language Features by Svetlana Isakova,特别是其中的“构建器推断”部分(在30分钟处)。


我认为应该分为两个回答,因为我认为第一个正确,但后者缺乏一些美感。 - Holger Brandl
1
@Lensflare * 的意思是将数组解构为单独的项目,例如 mutableListOf( * [1, 2, 3] ) 表示 mutableListOf(1, 2, 3),这就像是 vararg 的相反操作。 - Jacob Wu
1
@Jacob Wu:谢谢你的回答。通过你的回答,我找到了这个操作符被称为“展开操作符”。我明白它可以将一些参数与数组合并成可变参数列表来帮助我们。但是在你的例子中它有什么好处吗?它会更快吗?还是确保了集合被复制的关键? - Lensflare
它是低效的。与Java的List.addAll相同 https://stackoverflow.com/questions/54381327/difference-between-arraylist-addalllist-and-new-arraylistlist-for-content 使用https://dev59.com/11YN5IYBdhLWcg3w07Bx#46846074进行测试 https://try.kotlinlang.org/#/UserProjects/pllbim18v1vfa6one4i8l0hasj/497pnhfpsmvb73sn2378cv692d - user25
复制非基元对象列表时要小心。新列表将包含与旧列表相同的对象,因此更改一个列表中的项目将会影响两个列表中的项目。真正的深层复制还将复制列表项。请参见https://dev59.com/plUK5IYBdhLWcg3wqBbE。 - Big McLargeHuge
显示剩余2条评论

20
就像在Java中一样: 列表:
    val list = mutableListOf("a", "b", "c")
    val list2 = ArrayList(list)

地图:

    val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
    val map2 = HashMap(map)

假设你的目标是JVM(或Android);我不确定它是否适用于其他目标,因为它依赖于ArrayList和HashMap的拷贝构造函数。


19

你可以使用提供的扩展函数Iterable.toMutableList(),它将为你提供一个新的列表。不幸的是,正如其签名和文档所建议的那样,它旨在确保Iterable是一个List(就像toString和许多其他to<type>方法一样)。没有什么能保证它会成为一个新的列表。例如,在扩展程序的开头添加以下行:if (this is List) return this是一种合法的性能改进(如果确实提高了性能)。

此外,由于其名称,产生的代码并不是非常清晰。

我更喜欢添加自己的扩展函数,以确保结果并创建一个更加清晰的代码(就像我们为数组所做的那样):

fun <T> List<T>.copyOf(): List<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

fun <T> List<T>.mutableCopyOf(): MutableList<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

请注意,addAll是最快的复制方式,因为在实现ArrayList的时候使用了本地System.arraycopy

此外,请注意这只会给你一个浅拷贝

编辑:

您可能希望使用更通用的版本:

fun <T> Collection<T>.copyOf(): Collection<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

fun <T> Collection<T>.mutableCopyOf(): MutableCollection<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

我喜欢这个解决方案。难道不应该是 addAll(this@copyOf) 吗,因为 apply 内部的 this 将会指向新创建的空列表?或者改为 mutableListOf<T>().also { it.addAll(this) } - Franko Leon Tokalić

9

如果要进行浅拷贝,我建议

.map{it}

这对许多集合类型都适用。


1
请注意,它不适用于“Map”。它可以编译,但由于“it”是“Map.Entry”,并且复制是浅层的,因此您将拥有相同的条目。 - noamtm
1
@noamtm 是的,这就是我所说的浅拷贝。这种方法永远不会复制条目。它只会将集合复制一份具有相同条目的副本。在这里,Map 没有什么特别之处。 - Lensflare
2
我的观点是,尽管在地图上使用它很诱人,而且编译并似乎可以工作 - 但它实际上并不起作用。 - noamtm

8
您可以使用 ArrayList 构造函数:ArrayList(list)

4
var oldList: List<ClassA>?
val newList = oldList.map { it.copy() }

3
val selectedSeries = listOf(*series.toTypedArray())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接