Java Arrays.copyOf是否生成浅拷贝或深拷贝?

Java Arrays.copyOf是否生成浅拷贝或深拷贝?,java,Java,关于array.copyOf是否会产生深层或浅层副本,似乎有很多困惑和不同的观点([1]和其他来源) 此测试表明副本很深: String[] sourceArray = new String[] { "Foo" }; String[] targetArray = java.util.Arrays.copyOf( sourceArray, 1 ); sourceArray[0] = "Bar"; assertThat( targetArray[0] ).isEqualTo( "Foo" );

关于
array.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]

它生成一个浅层副本,即一个包含“旧”引用的新数组(指向相同的对象,这些对象未被复制)

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

此测试表明副本很深:

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

sourceArray[0] = "Bar";

assertThat( targetArray[0] ).isEqualTo( "Foo" ); // passes
不,没有。将新对象指定给“原始”数组时,这不会影响副本。毕竟,这是一份副本

这与以下情况相同:

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

assertEquals(y, "foo");
这里没有“深度复制”。

表格

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


因此,对于包含引用的数组,只复制引用,而不复制实际对象。这意味着一个浅拷贝。

它是一个深拷贝。在弦的情况下,它看起来很浅,因为在盖下,弦是单态的。JVM有一个用于存储字符串的内存池,并且只为每个唯一字符串创建一个副本。所以你总是会得到一个字符串引用的副本。下面的示例显示为类对象创建了深度副本。更改原始阵列时,副本不会更改

公共类阵列测试{

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

}“浅”或“深”-我认为没有人精确定义这个问题-方法
数组。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]
当初始源数组项由“1”串联时

在重新分配源时,目标不再与源绑定的意义上,它也适用于对象数组。 但在复制和更改源[0]之后查看两个数组的第一个元素的输出可以揭示全部事实:

Source1 : java.lang.Object@1db9742
Target1 : java.lang.Object@1db9742
Source2 : java.lang.Object@106d69c
Target2 : java.lang.Object@1db9742
复制原始源数组后,目标元素只指向源数组中当前保存的值。对于目标[0],它是内存地址1db9742的内容——也是保存源[0]的同一内存地址

在源[0]被重新分配后,源和目标之间出现脱胶的原因是因为赋值语句

source[0] = new Object();
只会导致源[0]中保存的内存引用在指向新对象时更改为某个新位置。 因此,从纯意义上讲,它毕竟不是真正的深度复制,尽管在许多情况下,它给编码者带来了与深度复制相同的好处

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

所以Arrays.copyOf(..)对于基本体和一维对象数组来说都是“便宜”的深度拷贝。 但是,任何数据数组都会变得更复杂,并且会被发现


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

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

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

可能只是另一次实习Strings@Matthias例如我不这么认为。因为“Foo”是一个字面意思,它将被实习;测试假设。如果该假设是正确的,那么测试将调查目标元素是否被相应源元素的
=“Bar”
更改。我不知道测试在哪里对字符串是否内部化做出任何假设。我没有看到任何身份测试,我只看到平等测试。浅拷贝和深拷贝的结果是相同的,因为浅拷贝和深拷贝之间的相等性测试不能有所不同。人们需要身份测试来区分浅拷贝和深拷贝。它不是深拷贝,深拷贝需要对存储在数组中的所有对象进行拷贝。Arrays.copyOf不能做到这一点;它只是复制引用,即浅拷贝。此外,为什么字符串数组的行为会不同于其他对象的论点是完全错误的。是的,字符串字面值被占用。不,这不会导致字符串的行为与任何其他(引用)对象的行为有任何不同。可能这个回答者被问题代码依赖于文本的字符串内接误导了,这是第一次测试通过所必需的。如果将
“Foo”
放入一个变量中,然后在任何地方使用该变量,问题就会更清楚。很抱歉,您的答案不止一个方面是错误的。这本书是浅薄的。字符串并不总是内部化,只是在特殊情况下。例如,在
String foo=“foo”;字符串foo2=新字符串(foo)
foo2
表示新副本。而且不可能从一个被重写的
Object.equals()
方法得出副本是深的还是浅的结论,因为这是
Object.equals()
根据定义隐藏的东西。