1. 概述
在 Kotlin 中,复制一个 List
是一个常见但又容易被忽视的操作。本文将介绍几种常见的复制方式,包括浅拷贝与深拷贝,帮助你在实际开发中避免踩坑。
2. 浅拷贝 List
在 Kotlin 中,最简单的复制方式是使用 toList()
扩展函数:
val cities = listOf("Berlin", "Munich", "Hamburg")
val copied = cities.toList()
assertThat(copied).containsAll(cities)
✅ 这种方式会创建一个新的 List
实例,并将原 List
中的元素逐个放入新列表中。如果你需要一个可变列表,可以使用 toMutableList()
:
val mutableCopy = cities.toMutableList()
assertThat(mutableCopy).containsAll(cities)
⚠️ 注意:这两种方式都是浅拷贝。也就是说,新旧列表本身是两个不同的对象,但它们包含的元素引用是相同的。例如:
assertThat(copied).isNotSameAs(cities) // ✅ 不是同一个对象
assertThat(copied[0]).isSameAs(cities[0]) // ✅ 但元素引用相同
3. 深拷贝 List
当 List
中包含的是引用类型(即对象)时,浅拷贝无法满足需求,因为修改其中一个对象的属性会影响另一个列表中的对象。这时就需要进行深拷贝。
3.1. 使用 data class 的 copy 方法
如果列表中的元素是 data class
且仅包含基本类型或不可变类型(如 String
),可以使用其自带的 copy()
方法实现深拷贝:
data class Person(var name: String, var age: Int)
val persons = listOf(Person("Bob", 20), Person("Alice", 21))
val personsCopy = persons.map { it.copy() }
assertThat(personsCopy).isNotSameAs(persons) // ✅
assertThat(personsCopy[0]).isNotSameAs(persons[0]) // ✅
⚠️ 注意:copy()
方法只会对当前类的字段做浅拷贝。如果字段中包含其他对象,这些对象不会被深拷贝,仍需手动处理。
3.2. 自定义 clone 方法实现深拷贝
如果对象结构复杂,包含嵌套对象,就需要自定义 clone()
方法来逐层深拷贝:
data class Address(var streetName: String, var streetNumber: Int, var city: String)
data class Person(val name: String, val age: Int, val address: Address) {
fun clone(): Person {
return Person(this.name, this.age, this.address.copy())
}
}
使用示例:
val address1 = Address("Sesame Street", 1, "CartoonLand")
val address2 = Address("Sesame Street", 2, "CartoonLand")
val persons = listOf(Person("Bob", 20, address1), Person("Alice", 21, address2))
val personsCopy = persons.map { it.clone() }
assertThat(personsCopy).isNotSameAs(persons)
assertThat(personsCopy[0]).isNotSameAs(persons[0])
assertThat(personsCopy[0].address).isNotSameAs(persons[0].address)
assertThat(personsCopy[0].address).isEqualTo(persons[0].address)
修改拷贝后的地址:
personsCopy[0].address.streetNumber = 10
assertThat(personsCopy[0].address).isNotEqualTo(persons[0].address)
✅ 上述示例说明:通过逐层拷贝,我们实现了真正的深拷贝。如果 Address
本身也包含对象字段,也需要为其添加 copy()
或 clone()
方法。
3.3. 不可变类型下的浅拷贝是否安全?
Kotlin 中很多类型是不可变的(如 String
、val
定义的属性)。对于这些类型来说,即使进行浅拷贝也是安全的,因为它们的值无法被修改。
例如:
data class Person(val name: String, val age: Int)
✅ 因为 name
和 age
都是 val
,所以即使浅拷贝也不会导致意外修改。这种情况下无需深拷贝。
4. 小结
方法 | 是否深拷贝 | 适用场景 |
---|---|---|
toList() / toMutableList() |
❌ 浅拷贝 | 基本类型或不可变对象 |
map { it.copy() } |
⚠️ 部分深拷贝 | 简单 data class |
自定义 clone() 方法 |
✅ 真正深拷贝 | 嵌套对象结构复杂时 |
✅ 推荐做法:
- 对于不可变数据结构,使用
toList()
即可。 - 对于简单 data class,用
map { it.copy() }
。 - 对于复杂对象结构,建议手动实现
clone()
方法以确保深拷贝。
📌 原文链接:Copying a List in Kotlin
💡 作者邮箱:john.doe@example.com