Arrays.copyOf会产生浅拷贝还是深拷贝?

关于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中对二维数组进行深层复制?

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

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

此测试表明副本很深:

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

这与以下情况相同:

 String x = "foo"; String y = x; x = "bar"; assertEquals(y, "foo"); 

这里没有“深刻的副本”。

表格Java Doc

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

因此,在包含引用的数组的情况下,仅复制引用而不是实际对象。 这意味着浅拷贝。

‘浅’或’深’ – 这是我认为没有人精确定义的问题 – 方法Arrays.copyOf(..) DOES在实践中产生源数组的副本,它不受源数组更改的影响。

使用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的“深层复制”时,他们只想要一些不受原始变化影响的东西。

而这个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”时。

它也适用于Object数组,因为当重新分配目标时,目标不再与源绑定。 但是在复制之后查看两个数组的第一个元素的输出,然后在更改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的内容 - 它也是保持源[0]的相同内存地址。 。 。 。

我们在source [0]重新分配之后在源和目标之间得到剥离的原因是由于赋值语句的事实

 source[0] = new Object(); 

只需使源[0]中保存的内存引用更改为某个新位置,因为正在指向新的Object。 因此,它纯粹意义上并不是真正的深层复制,即使在许多情况下它给编码器带来了与深层复制相同的好处。

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

所以Arrays.copyOf(..)是原始和1-D对象数组的“廉价”深度副本。 但是任何数据arrays都比较复杂,而且它被发现了。

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

它创建Shallow副本,因为但是由于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 

这是一个深刻的副本。 在字符串的情况下它看起来很浅,因为在封面下,字符串是单身人士。 JVM有一个用于Strings的内存池,并且只为每个唯一字符串创建一个副本。 所以你总是得到对该字符串的引用的副本。 下面的示例显示了为Object类创建了一个深层副本。 更改原始数组时,副本不会更改。

public class 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()); } 

}