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

时间:2017-07-20 05:54:07

标签: list collections kotlin deep-copy

该元素已实现深度复制。

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

我正在使用这种代码,但似乎很冗长。

2 个答案:

答案 0 :(得分:2)

由于类型系统的限制,这个问题不能在没有绕过类型安全的情况下推广到单个功能(并且由于JVM类型擦除,你绝对不希望那个兔子洞仿制药涉及¹)。

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

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由于类型干扰模糊而无效)。类型别名用于防止水平空间爆炸³,并且函数集通过通用复制函数参数与深度可复制接口解耦。

使用示例:

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")
}

- &GT;

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>的转换将始终在运行时成功,但稍后在访问转换列表时失败。实现提到的魔术“单一功能”是可能的,但是最轻微的错误会导致返回的数据结构在使用类强制转换异常访问时看似“随机”失败。

[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>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

答案 1 :(得分:0)

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

Noet clone方法执行此对象的副本,而非复制操作,但您可以覆盖clone方法以提供深度复制操作,例如:

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
    }
}