【问题标题】:Java - Implement deep and shallow copy of an arrayJava - 实现数组的深拷贝和浅拷贝
【发布时间】:2016-12-03 03:33:52
【问题描述】:

我试图理解 Java 中浅拷贝和深拷贝的概念。 有很多关于这个主题的文章和问答,但是每当我尝试在真实的 Java 代码中实现这些概念时,一切都变得不清楚。

我的理解所依据的答案之一是 this link,其中通过模式解释了深复制和浅复制。

我将在下面为您展示每种情况的实现:

  • 浅拷贝:

我以方法 System.arraycopy() 为例,因为我在许多文章中读到它执行浅拷贝(以及 clone 方法)

public class Test {

    public static void main(String[] args) {
        NameValue[] instance1 = {
                new NameValue("name1", 1),
                new NameValue("name2", 2),
                new NameValue("name3", 3),
        };
        NameValue[] instance2 = new NameValue[instance1.length];

        // Print initial state
        System.out.println("Arrays before shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));

        // Perform shallow copy
        System.arraycopy(instance1, 0, instance2, 0, 3);

        // Change instance 1
        for (int i = 0; i < 3; i++) {
            instance1[i].change();
        }

        // Print final state
        System.out.println("Arrays after shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));
    }

    private static class NameValue {
        private String name;
        private int value;

        public NameValue(String name, int value) {
            super();
            this.name = name;
            this.value = value;
        }

        public void change() {
            this.name = this.name + "-bis";
            this.value = this.value + 1;
        }

        @Override
        public String toString() {
            return this.name + ": " + this.value;
        }
    }
}

主要方法的执行结果如下:

Arrays before shallow copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after shallow copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]

此结果与上一个链接的架构一致:

  • 深拷贝:

我在这个例子中使用了 Arrays.copyOf() 方法,因为我在许多文章中读到它执行深度复制(以及 Arrays.copyOfRange 方法)

public static void main(String[] args) {
    NameValue[] instance1 = {
            new NameValue("name1", 1),
            new NameValue("name2", 2),
            new NameValue("name3", 3),
    };
    NameValue[] instance2 = new NameValue[instance1.length];

    // Print initial state
    System.out.println("Arrays before deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));

    // Perform deep copy
    instance2 = Arrays.copyOf(instance1, 3);

    // Change instance 1
    for (int i = 0; i < 3; i++) {
        instance2[i].change();
    }

    // Print final state
    System.out.println("Arrays after deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));
}

显示:

Arrays before deep copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after deep copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]

如果我们将深拷贝逻辑基于之前的模式,结果应该是这样的:

你可能注意到了,main方法的执行结果和上面schema的逻辑是不一样的。

欢迎任何解释。

【问题讨论】:

  • 哇,你真的在​​这里花了很多时间。希望您实际上并没有提供 太多 信息(例如,我更想跳到下一个问题,只是因为我可能需要 10 分钟来消化您的输入) .但只是为了它:尝试使用 clone 方法而不是 copyOf() (我认为这不会浪费时间克隆东西,而只是做一个浅拷贝)
  • Arrays.copyOf 进行浅拷贝。
  • 只能通过比较两个实例来判断是深拷贝还是浅拷贝。基本上,它们应该具有“相同的内容”,但如果它是深层副本,则不应引用相同的对象。如果您使用 Java(或其他语言以及它们如何处理它)进行开发,您应该了解一些关于引用和东西的事情。
  • @sascha10000 我误解了一些事情,因为我的逻辑基于Arrays.copyOf 执行深层复制这一事实。现在你们中的许多人确认它会产生浅拷贝,事情就更清楚了

标签: java arrays deep-copy shallow-copy


【解决方案1】:

我不知道你在哪里看到 copyOf() 执行深层复制,因为那完全是错误的。

引用Arrays.copyOf(T[] original, int newLength)的javadoc:

对于在原始数组和副本中都有效的所有索引,两个数组将包含相同的值

这意味着它是一个浅拷贝。要成为深层副本,值必须指向不同的对象,因为引用的对象也必须是副本。

要执行深层复制,必须迭代数组并复制值。 Java 无法为您做到这一点,因为它不知道如何复制对象。

例如Java 怎么知道如何复制NameValue 对象? clone()?复制构造函数?序列化+反序列化?工厂方法?其他方式?

【讨论】:

  • 添加到您的评论中,如果您搜索copyOf() 方法的源代码,您最终会发现:System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); - 最后,两者都是相同的:P跨度>
  • 是的。 copyOf() 方法只是 System.arraycopy() 的便捷方法。
  • 不幸的是,Java 对于深度复制的东西绝对是 AWFUL。正如 Andreas 所说,必须自己迭代和复制所有内容。您可以尝试 Cloneable 的东西,但它并没有真正起作用。
  • @Andreas @g0dkar 我从作者 Richard M. Reese 的“Oracle Certified Associate, Java SE 7 Programmer Study Guide”一书中获得了这些信息。在第 4 章的复制数组部分的总结中,评论说 使用Arrays.copyOf 方法执行整个数组的深层复制...基于我的逻辑关于这个信息,我没有走太远去寻找Arrays.copyOf方法的实现
  • @Dispersia Primitives 是普通值,浅与深不适用。该值被简单地复制。区别适用于对象引用,区分复制 reference(浅)和复制 object(深)。
【解决方案2】:

我试图理解 Java 中浅拷贝和深拷贝的概念。

在 Java 中,您传递和存储对对象的引用而不是对象本身。
因此,当您有 NameValue[] array 时,该数组不包含对象 NameValue,而是对对象的引用。
因此,当您对NameValue[] array2 进行浅拷贝时,这意味着您只是将引用从一个数组复制到另一个数组。这实际上意味着现在arrayarray2 都引用完全相同的对象,并且您对array[2] 所做的任何更改都将在array2[2](相同的对象)中可见。

当您进行深度复制时,您会将每个对象完全复制到另一个内存区域,并在新数组中保留对该新对象的引用。
这样,2 个数组现在引用不同的对象,对 array[2] 的任何更改都不会从 array2[2] 看到

更新:
这不适用于存储实际值而非引用的原语。
因此,int[] a 在复制时会得到值的副本(即某种意义上的深层复制),因为a[2] 包含值本身而不是对值的引用。

【讨论】:

  • 很好的解释。有人应该提到,JDK 中的每个复制方法实际上都会复制它的内容。但是,当您复制对象数组而不是原语时,浅复制和深复制之间的区别就变得很明显了。
  • 这是我一直在寻找的解释,尤其是 Java 处理使用原语复制数组的方式与使用引用复制数组的方式......连同@Andreas 的答案,我假设一方面我在问题中引用的方法在使用带有引用的数组时会产生浅拷贝,另一方面,在使用原语时它们会产生“更像深拷贝”
  • @AnassBouchkioui:原始变量是内存中包含您分配的值的位置。对象变量是内存中包含对实际对象位置的引用的位置。因此,当您分配一个变量时,您会将一个地方的内容移动到另一个地方。在原始的情况下,内容就是价值本身。在引用的情况下,内容是对象的引用。
【解决方案3】:

我认为Arrays.copyOf()产生深拷贝有点误解。

Arrays.copyOf() 创建一个新数组,其中包含对未复制对象的旧引用,正如我添加的链接所解释的,在嵌套数组的情况下它们不会被复制,因此不能考虑副本,但浅副本。

更多信息请参见this

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-19
    • 2012-04-13
    相关资源
    最近更新 更多