18

Arrays.copyOf关于是否会产生深拷贝或浅拷贝,似乎有很多混淆和不同的意见([1] 和其他来源) 。

此测试表明副本很深:

String[] sourceArray = new String[] { "Foo" };
String[] targetArray = java.util.Arrays.copyOf( sourceArray, 1 );

sourceArray[0] = "Bar";

assertThat( targetArray[0] ).isEqualTo( "Foo" ); // passes

该测试表明副本很浅:

String[][] sourceArray = new String[][] { new String[] { "Foo" } };
String[][] targetArray = java.util.Arrays.copyOf( sourceArray, 1 );

sourceArray[0][0] = "Bar";

assertThat( targetArray[0][0] ).isEqualTo( "Foo" ); // fails

解决方案是不是简单地做一个顶层维度的深拷贝,而其他维度是浅拷贝?真相是什么?

[1]如何在 Java 中进行二维数组的深拷贝?

4

5 回答 5

26

它产生一个浅拷贝,即一个包含“旧”引用的数组(对于相同的对象,那些没有被复制)。

特别是,如果您有嵌套数组,则不会复制这些数组。您将获得一个新数组,其“顶级”指向与原始数组相同的“二级”数组。这些嵌套数组中的任何更改都将反映在副本和原始数组中。

此测试表明副本很深:

不,不是的。当您将新对象分配给“原始”数组时,这不会影响副本。毕竟,它是一个副本。

这与以下情况相同:

String x = "foo";
String y = x;
x = "bar";

assertEquals(y, "foo");

这里没有“深拷贝”。

于 2013-08-21T07:52:45.487 回答
5

形成Java 文档

....这两个数组将包含相同的值。

因此,如果数组包含引用,则仅复制引用而不复制实际对象。这意味着浅拷贝。

于 2013-08-21T07:54:05.560 回答
2

“浅”或“深” - 这是我看到没有人精确定义的问题 - 该方法Arrays.copyOf(..)实际上会生成源数组的副本,该副本不受源数组更改的影响。

使用以下 int 数组的简单示例:

import java.util.Arrays;

public class DeepCopyTest
{

    public static void main(String[] args)
    {
       int[] source = { 1, 2, 3, 4, 5, 6};
       int[] target = new int[source.length];
       // Copy target from source via Arrays.copyOf(..) method :
       target = Arrays.copyOf(source, 6);
       // Check for equality :
       System.out.println("Source1 : " + Arrays.toString(source));
       System.out.println("Target1 : " + Arrays.toString(target));
       // Increment all entries in source array :
       for(int i = 0; i < source.length; i++)
       {
          source[i] = source[i] +1;
       }
       // See if target is affected :
       System.out.println("Source2 : " + Arrays.toString(source));
       System.out.println("Target2 : " + Arrays.toString(target));

    }

}

// OUTPUT
// ------
Source1 : [1, 2, 3, 4, 5, 6]
Target1 : [1, 2, 3, 4, 5, 6]
Source2 : [2, 3, 4, 5, 6, 7]
Target2 : [1, 2, 3, 4, 5, 6]

在实践中,当人们寻求数组的“深拷贝”时,他们只想要不受原始更改影响的东西。

而这个 Arrays.copyOf(..)` 方法确实给了他们这个。

与原始类型数组一样,String 对象数组的行为也与上面的示例一样,输出如下:

Source1 : [a, b, c, d, e, f]
Target1 : [a, b, c, d, e, f]
Source2 : [a1, b1, c1, d1, e1, f1]
Target2 : [a, b, c, d, e, f]

当初始源数组条目由“1”连接时。

它也适用于对象数组,因为当重新分配后者时,目标不再与源绑定。但是在复制之后查看两个数组的第一个元素的输出,然后在更改 source[0] 之后显示全部真相:

Source1 : java.lang.Object@1db9742
Target1 : java.lang.Object@1db9742
Source2 : java.lang.Object@106d69c
Target2 : java.lang.Object@1db9742

在复制原始源数组后,目标元素只是简单地指向了它们的源对应项中当前保存的任何值。对于 target[0] 它是内存地址 1db9742 的内容——这也是保存 source[0] 的相同内存地址。. . .

在重新分配 source[0] 后,我们在源和目标之间解除绑定的原因是因为赋值语句

source[0] = new Object();

只是导致在 source[0] 中保存的内存引用在指向新对象时更改为某个新位置。所以它毕竟不是纯粹意义上的真正的深拷贝,尽管在许多情况下它给编码者带来了与深拷贝相同的好处。

对于原始数据数组,Arrays.copyOf(..) 方法不能复制引用,因为这些引用不用于原始数据。它只是将源元素值复制到目标元素中。同样,我们与深拷贝具有相同的效果,但代价是操作需要的代码比深拷贝少得多。

因此 Arrays.copyOf(..) 是原始数组和一维对象数组的“廉价”深拷贝。但是任何更复杂的数据数组都会被发现。

也许它应该被称为半深拷贝。

于 2016-06-29T15:33:11.710 回答
0

它创建了浅副本,因为但是由于 java 按值使用参数,所有变量的副本在克隆对象中可用,但是对于引用类型变量,地址的副本被创建并指向由原始数组引用的相同对象,因此当复制对象被修改时数组中的原始对象也得到更新。请参阅下面的代码。

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class ArraysCopyOfDemo
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Object[] originalArray= new Object[1];
        Employee e1= new Employee("Salman","Khan");
        originalArray[0]=e1;
        System.out.println("Original Array content printed ");
        printArray(originalArray);

      Object[] copiedArray=originalArray.clone();
        System.out.println("Copied Array content printed ");
        printArray(copiedArray);
        System.out.println("Copied Array content modified ");
        Employee CopiedEmp1= (Employee)copiedArray[0];
        CopiedEmp1.setFirstname("Amir");
        System.out.println("Copied Array content printed ");
        printArray(copiedArray);
        System.out.println("Original Array content printed to verify shallow copy or deep copy");
        printArray(originalArray);
    }
    private static void printArray(Object[] arrays ){
        for(Object emp:arrays){
            System.out.print(((Employee)emp).getFirstname() + " ");
            System.out.print(((Employee)emp).getLastname());
            System.out.println();
        }
    }
}
class Employee implements Cloneable{
    private String firstname;
    private String lastname;
    public Employee(String firstname,String lastname){
        this.firstname=firstname;
        this.lastname=lastname;
    }
    public String getFirstname(){
       return firstname;
    }
    public String getLastname(){
        return lastname;
    }
    public void setFirstname(String firstname){
        this.firstname=firstname;
    }
     public void setLirstname(String lastname){
         this.lastname=lastname;
    }

}

O/p
Original Array content printed 
Salman Khan
Copied Array content printed 
Salman Khan
Copied Array content modified 
Copied Array content printed 
Amir Khan
Original Array content printed to verify shallow copy or deep copy
Amir Khan
于 2016-11-18T08:56:44.887 回答
-4

这是一个深拷贝。它在字符串的情况下显得很浅,因为在幕后,字符串是单例。JVM 有一个用于字符串的内存池,并且每个唯一字符串只制作一个副本。因此,您始终会获得对该字符串的引用的副本。下面的示例显示为 Object 类进行了深层复制。当原始数组更改时,副本不会更改。

公共类 ArrayTest {

public static void main(String [] args) {
    Object [] objs = new Object[1];
    objs[0] = new Object();
    System.out.println("Original: " + objs[0].toString());

    Object [] copy = java.util.Arrays.copyOf(objs, 1);
    objs[0] = new Object();
    System.out.println("copy, after original reassigned: " +
    copy[0].toString());
    System.out.println("Original, after reassigned: " +
    objs[0].toString());
}

}

于 2014-09-08T01:47:05.430 回答