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 中很多类型是不可变的(如 Stringval 定义的属性)。对于这些类型来说,即使进行浅拷贝也是安全的,因为它们的值无法被修改。

例如:

data class Person(val name: String, val age: Int)

✅ 因为 nameage 都是 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


原始标题:Copying a List in Kotlin