在Kotlin中克隆列表:深拷贝与浅拷贝

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

在Kotlin中克隆列表:如何避免引用问题?

\n\n

想象一下:你正在用Kotlin编写一个应用程序,处理一个数据列表,突然原始数组自己发生了变化。是不是很熟悉?很可能你只是复制了引用,而不是列表本身。在本文中,我们将解析所有合法的在Kotlin中克隆列表的方法——从浅拷贝到深拷贝。没有废话,只有代码和解释。

\n\n

这个主题源于Stack Overflow上一个热门问题(超过20万次浏览):"How to clone or copy a list in kotlin"。让我们一劳永逸地弄清楚如何正确复制Kotlin列表,以免丢失数据或抓狂。

\n\n

为什么简单的赋值不起作用?

\n\n

在Kotlin中,和Java一样,列表是引用类型。如果你这样写:

\n\n
val original = mutableListOf("A", "B", "C")\nval copy = original  // 这不是拷贝!\ncopy.add("D")\nprintln(original) // [A, B, C, D] — 原列表被修改了!
\n\n

你只是创建了指向内存中同一对象的第二个引用。任何通过copy进行的修改都会反映到original上。为了避免这种情况,需要使用专门的Kotlin列表拷贝方法。

\n\n

在Kotlin中复制列表的5种最佳方法

\n\n

我们将介绍所有主要选项:从简单的toList()到手动的深拷贝。根据你的需求选择合适的方法。

\n\n

1. toList() — 最简单的方法

\n\n

toList()方法创建一个新的不可变列表副本。这是浅拷贝(shallow copy)——元素本身不会被复制,但列表结构变得独立。

\n\n
val original = listOf("Kotlin", "Java", "Python")\nval copy = original.toList()\n\n// copy 是一个新对象\nprintln(copy === original) // false — 不同的引用\nprintln(copy == original)  // true — 相同的内容
\n\n

非常适合简单的字符串或数字列表。如果你需要一个可变副本,请使用toMutableList()。

\n\n

2. toMutableList() — 用于可变副本

\n\n

想要一个可以修改的副本?toMutableList()是你的选择。它创建一个包含相同元素的新MutableList。

\n\n
val original = listOf(1, 2, 3)\nval mutableCopy = original.toMutableList()\nmutableCopy.add(4)\n\nprintln(original)       // [1, 2, 3]\nprintln(mutableCopy)    // [1, 2, 3, 4] — 原列表未受影响
\n\n

这是在Kotlin中克隆可变列表最流行的方法。

\n\n

3. ArrayList(collection) — Java经典方式

\n\n

如果你来自Java,这个方法会感觉很熟悉。ArrayList构造函数接受另一个集合并复制其元素。

\n\n
val original = listOf("A", "B", "C")\nval arrayCopy = ArrayList(original)\narrayCopy[0] = "Z"\n\nprintln(original)    // [A, B, C]\nprintln(arrayCopy)   // [Z, B, C]
\n\n

运行快速且可预测。但请记住:这也是浅拷贝。

\n\n

4. map() — 带元素转换

\n\n

如果你不仅需要复制,还需要转换元素,请使用map()。它基于旧列表创建一个新列表,并对每个元素应用lambda表达式。

\n\n
val numbers = listOf(10, 20, 30)\nval doubled = numbers.map { it * 2 }\n\nprintln(numbers)  // [10, 20, 30]\nprintln(doubled)  // [20, 40, 60]
\n\n

对于无需修改的简单复制,可以写numbers.map { it },但这有些冗余——最好使用toList()。

\n\n

5. 复杂对象的深拷贝(deep copy)

\n\n

之前的所有方法都是浅拷贝。如果你的列表包含可变对象(例如带有字段的data class),那么修改副本中的对象会影响原始对象:

博客

书籍推荐