【问题标题】:Does Arrays.copyOf produce a shallow or a deep copy?Arrays.copyOf 会产生浅拷贝还是深拷贝?
【发布时间】:2013-08-23 11:43:30
【问题描述】:

对于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]How do I do a deep copy of a 2d array in Java?

【问题讨论】:

  • 可能只是另一个字符串的实习
  • @Matthias:我不这么认为。由于“Foo”是一个字面量,它会被实习;测试假设。如果该假设正确,则测试将调查目标元素是否已被相应源元素的= "Bar" 更改。
  • 我看不出测试在哪里对字符串是否被内化做出任何假设。我没有看到任何身份测试,我只看到平等测试。浅拷贝和深拷贝的结果是相同的,因为相等性测试在浅拷贝和深拷贝之间不能不同。需要身份测试来区分浅拷贝和深拷贝。

标签: java


【解决方案1】:

它创建了浅拷贝,因为但由于 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

【讨论】:

    【解决方案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(..)` 方法确实给了他们这个。

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

    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 的内容——这也是保存 source[0] 的相同内存地址。 . . .

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

    source[0] = new Object();
    

    当一个新的对象被指向时,只是导致 source[0] 中保存的内存引用被更改到某个新位置。 因此,它毕竟不是纯粹意义上的真正深拷贝,尽管在许多情况下它为编码人员提供了与深拷贝相同的好处。

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

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

    或许应该叫半深拷贝。

    【讨论】:

      【解决方案3】:

      这是一个深拷贝。它在字符串的情况下显得很浅,因为在幕后,字符串是单例。 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());
      }
      

      }

      【讨论】:

      • 这不是深拷贝,深拷贝需要复制存储在数组中的所有对象本身。 Arrays.copyOf 不这样做;它只是复制引用 - 即浅拷贝
      • 此外,为什么字符串数组的行为与其他对象不同的论点是完全错误的。是的,字符串文字是实习的。不,这不会导致字符串的行为与任何其他(引用)对象有任何不同。可能这个回答者被问题代码对文字字符串实习的依赖所误导,这是第一次测试通过所必需的。如果将"Foo" 放入变量中,然后在任何地方使用该变量,问题就会更清楚。
      • 很抱歉,您的答案不止一个方面是错误的。副本是浅拷贝。字符串并不总是内化,只有在特殊情况下。例如,在String foo = "Foo"; String foo2 = new String(foo); 中,foo2 指的是新副本。并且不可能从覆盖的 Object.equals() 方法得出结论是副本是深还是浅,因为这是 Object.equals() 根据定义隐藏的东西。
      【解决方案4】:

      表格Java Doc

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

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

      【讨论】:

        【解决方案5】:

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

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

        这个测试表明副本很深:

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

        这与以下情况相同:

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

        这里没有“深拷贝”。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-30
          • 2011-10-13
          • 1970-01-01
          • 1970-01-01
          • 2010-11-19
          • 1970-01-01
          相关资源
          最近更新 更多