【问题标题】:System.arrayCopy() copies object or reference to object?System.arrayCopy() 复制对象或对对象的引用?
【发布时间】:2013-02-14 14:50:38
【问题描述】:

我有最后一节课NameAndValue。我使用System.arrayCopy() 复制了一个NameAndValue 对象数组,当我更改复制数组中的NameAndValue 对象时,它会反映在原始数组中。

public final class NameAndValue {
    public String name;
    public String value;

    public NameAndValue() { }

    public NameAndValue(String name, String value) {
        this.name = name;
        this.value = value;
    }
}
public class Main {
    public static void main(String[] args) {
        NameAndValue[] nv = new NameAndValue[4];
        nv[0] = new NameAndValue("A", "1");
        nv[1] = new NameAndValue("B", "2");
        nv[2] = new NameAndValue("C", "3");
        nv[3] = new NameAndValue("D", "4");

        NameAndValue[] nv2 = new NameAndValue[4];
        System.arraycopy(nv, 0, nv2, 0, 2);
        nv2[2] = new NameAndValue("Y", "25");
        nv2[3] = new NameAndValue("Z", "26");

        for (int i = 0; i < 2; i++) {
            NameAndValue[] nv3 = new NameAndValue[4];
            System.arraycopy(nv2, 0, nv3, 0, 4);
            nv3[2].value = String.valueOf(i);
            nv3[3].value = String.valueOf(i + 1);

            System.out.println(nv2[2].value);
            System.out.println(nv2[3].value);
            System.out.println("-----------------------");
        }
    }
}

我得到的输出是:

0
1
-----------------------
1
2
-----------------------

在所有情况下我都应该得到 25 和 26 的输出,对吗?为什么会变??

【问题讨论】:

    标签: java arrays object copy deep-copy


    【解决方案1】:
    int[] a = {1, 2, 3};
    int[] b = new int[3];
    System.arraycopy(a, 0, b, 0, 3);
    b[1] = 0;
    System.out.println(Arrays.toString(b));
    System.out.println(Arrays.toString(a));
    

    输出是这样的:

    [1, 0, 3]
    [1, 2, 3]
    

    【讨论】:

    • 这只是表明它做了一个浅拷贝。你的结构没有深度。如果确实有深度,您会发现 System.arraycopy 不会进行深度复制。
    • 是的,你是对的。我测试了不更改数组元素的引用,而是更改了引用的数据的内容。我发现这两个数组值是相同的。谢谢!
    【解决方案2】:

    System.arraycopy 方法不执行数组的深度复制Arrays.copyOf 方法也不执行。它将 references 复制到对象,并且不创建新实例。如果您修改其中一个数组中的任何对象,它会影响两个数组:

    public static void main(String[] args) {
        NameAndValue[] arr1 = new NameAndValue[2];
        arr1[0] = new NameAndValue("A", "1");
        arr1[1] = new NameAndValue("B", "2");
    
        NameAndValue[] arr2 = new NameAndValue[2];
        System.arraycopy(arr1, 0, arr2, 0, 2);
        arr2[0].value = "3";                       // modify existing object
    
        NameAndValue[] arr3 = new NameAndValue[2];
        System.arraycopy(arr1, 0, arr3, 0, 2);
        arr3[1] = new NameAndValue("Y", "5");      // create new instance
    
        System.out.println(Arrays.toString(arr1)); // [A:3, B:2]
        System.out.println(Arrays.toString(arr2)); // [A:3, B:2]
        System.out.println(Arrays.toString(arr3)); // [A:3, Y:5]
    }
    
    public static final class NameAndValue {
        public String name;
        public String value;
    
        public NameAndValue(String name, String value) {
            this.name = name;
            this.value = value;
        }
    
        @Override
        public String toString() {
            return name + ":" + value;
        }
    }
    

    另见:
    Why does Array.copyOf() mutate the original array in case of 2D Arrays?
    How do I do a deep copy of a 2d array in Java?

    【讨论】:

      【解决方案3】:

      System.arraycopy 是一个浅拷贝。原因如下:

      它使用 JNI 来使用类似 memcpy() 的东西,它只是复制内存中的指针。这是一个非常快速的操作。

      【讨论】:

      • 在 Java 11(例如)中,arraycopy@HotSpotIntrinsicCandidate 注释,这可能意味着它不再使用 JNI。 (但绝对是原生代码实现。)
      【解决方案4】:

      System.arrayCopy() 复制对象还是对对象的引用?

      参考,这是一个的副本。令人惊讶的是,the docs don't say that explicitly,只是隐含地因为他们谈论复制数组元素,而不是递归地复制它们引用的东西。

      就像你有这个一样:

      NameAndValue nv1 = new NameAndValue("A", "1");
      NameAndValue nv2 = nv1;
      nv2.value = "4";
      System.out.println(nv1.value); // 4
      

      每个数组元素就像上面的 nv1nv2 变量。正如nv1nv2 引用(指向)相同的底层对象一样,数组条目也是如此,包括将这些条目从一个数组复制到另一个数组时。

      【讨论】:

        【解决方案5】:

        据我所知arraycopy 是浅拷贝。这意味着新数组将引用旧数组中元素的地址。因此,如果您在其中一个中调整一个值,它将同时反映在两者上

        【讨论】:

          【解决方案6】:

          它发生了变化,因为两个数组仍然引用相同的底层对象。您可以将一个新对象分配给数组中的某个位置,并且它不会反映在另一个数组中,但是如果您对两个数组指向的对象进行修改,那么两者都会以不同的方式看到它。

          从这个意义上说,它是“按值引用”传递,而不是严格按引用传递 - 但您看到的效果是因为引用保持不变。

          除非另有明确说明,否则在 Java 中这样的副本几乎总是浅副本。这也不例外。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-08
            • 1970-01-01
            • 2017-08-30
            • 2011-09-21
            • 1970-01-01
            • 2014-05-16
            • 2015-03-06
            • 1970-01-01
            相关资源
            最近更新 更多