深度复制 2D MutableList 的简洁方法是什么?

What is concise way of deep-copying a 2D MutableList?

该元素已经实现了深度复制。

fun <T : DeepCopiable> f(a: MutableList<MutableList<T>>) {
    val copied = a.map { it.map { it.deepCopy() }.toMutableList() }.toMutableList()
    ...
}

我正在使用这种代码,但它看起来很冗长。

由于类型系统的限制,这个问题不能在不绕过类型安全的情况下推广到单个函数(并且由于 JVM 类型擦除,你绝对不想去 that 涉及泛型时的兔子洞¹)。

但是,您可以编写一个 扩展函数来实现深度复制模式,在类型安全的情况下,将维度的每次增加委托给前一个函数:

private typealias I<E> = Iterable<E>
private typealias Copy<E> = (E) -> E
private inline fun <T, R> I<T>.mapToMutable(transform: (T) -> R): I<R> = mapTo(mutableListOf(), transform)
fun <E> I<E>.deepCopy1(c: Copy<E>) = mapToMutable { c(it) }
fun <E> I<I<E>>.deepCopy2(c: Copy<E>) = mapToMutable { it.deepCopy1(c) }
fun <E> I<I<I<E>>>.deepCopy3(c: Copy<E>) = mapToMutable { it.deepCopy2(c) }
fun <E> I<I<I<I<E>>>>.deepCopy4(c: Copy<E>) = mapToMutable { it.deepCopy3(c) }
fun <E> I<I<I<I<I<E>>>>>.deepCopy5(c: Copy<E>) = mapToMutable { it.deepCopy4(c) }

由于 JVM 类型擦除,函数需要不同的名称(@JVMName 由于类型干扰歧义而无济于事²)。类型别名用于防止水平 space 爆炸³,并且函数集通过通用复制函数参数与深度可复制接口分离。

用法示例:

fun main(args: Array<String>) {
    data class IntHolder(var value: Int)
    val original = List(3) { a ->
        List(3) { b ->
            IntHolder(a + b)
        }
    }

    val copied = original.deepCopy2 { it.copy() }
    original[0][0].value = 18258125
    println("original=$original")
    println("copied  =$copied")
}

->

original=[[IntHolder(value=18258125), IntHolder(value=1), IntHolder(value=2)], [IntHolder(value=1), IntHolder(value=2), IntHolder(value=3)], [IntHolder(value=2), IntHolder(value=3), IntHolder(value=4)]]
copied  =[[IntHolder(value=0), IntHolder(value=1), IntHolder(value=2)], [IntHolder(value=1), IntHolder(value=2), IntHolder(value=3)], [IntHolder(value=2), IntHolder(value=3), IntHolder(value=4)]]

[1]:因为泛型类型转换是由编译器在运行时执行的,所以从 List<Foo>List<Baz> 的转换在运行时总是会成功,但稍后在访问转换列表时会失败.实现上述魔法 "single function" 是可能的,但最轻微的错误都会导致返回的数据结构在访问时看似 "random" 失败,并出现 class 强制转换异常。

[2]:类型Iterable<Iterable<Foo>>的值同时满足

fun <T> Iterable<T>.baz() (T = Iterable<Foo>) 和
fun <T> Iterable<Iterable<T>.baz() (T = Foo)

因此,如果链中的所有方法都具有相同的函数名称,但 JVM 名称不同,编译器将无法确定要使用的正确方法。

[3]:

Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<Iterable<ithinkyougetthepoint>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

这是一个简单的深度复制示例,使用 java.lang.reflect.Array & java.lang.Cloneable

Noetclone method performs a shallow copy of this object, not a deep copy operation, but you can override the clone方法提供了deep复制操作,例如:

val list = mutableListOf(mutableListOf(arrayOf(1)))
val copied = list.deepCopy()

println(copied !== list) //true: not the same
println(copied.map{it.map{it.toList()}} == list.map{it.map{it.toList()}}) 
//                                      ^---true: content equals

//                v--- Array is cloned, since it has implemented Cloneable
println(copied[0][0] !== array[0][0]) // true

typealias NativeArray = java.lang.reflect.Array
@Suppress("UNCHECKED_CAST")
fun <T> T.deepCopy(): T {
    return when (this) {
        is Array<*> -> {
            val type = this.javaClass.componentType
            NativeArray.newInstance(type, size).also {
                this.forEachIndexed { i, item ->
                    NativeArray.set(it, i, item.deepCopy())
                }
            } as T
        }
        is MutableList<*> -> this.mapTo(mutableListOf()) { it.deepCopy() } as T
        is List<*> -> this.map { it.deepCopy() } as T
        is Cloneable -> this.javaClass.getDeclaredMethod("clone").let {
            it.isAccessible = true;
            it.invoke(this) as T
        }
        else -> this
    }
}