Java 的 ArrayList 的复制构造函数是浅拷贝还是深拷贝?

Is the copy constructor of Java's ArrayList shallow copy or deep copy?

提问人:蔡纪轮 提问时间:12/18/2017 更新时间:8/14/2018 访问量:4122

问:

长期以来,我一直对 Java 容器的复制构造函数是浅拷贝还是深拷贝感到困惑? 以下是我的理解: ints1、ints2、ints3 是引用,因此它们位于堆栈中。 inst1 指向堆中的某个对象,该对象包含位于堆栈中的三个匿名引用,它们分别指向 int 值为 0、1、2 的对象。

ints2 = ints1

因此,ints2 指向与 ints1 相同的对象。因此,更改 ints2 中引用指向的对象将影响 ints2 中的对象。

ints2.set(1,0+10)

将更改引用 ints1.get(0) 的对象。

ints3 = new ArrayList<>(ints1)

接下来是我的困惑。

如果 copy 构造函数是 shallow copy,那么尽管 ints1 和 ints3 指向不同的对象,但这两个对象具有相同的引用!因此,任何通过操作 ints1 中的引用来更改对象的操作都会更改 ints3,因为它们指向相同的对象。

如果 copy 构造函数是 deep copy,则 ints1 和 ints3 将保存不同的引用并指向不同的对象。那么 ints1 的变化不会影响 ints3 中的更改。

根据结果,复制构造函数似乎是深拷贝,而不是浅拷贝。

希望有人能纠正我,谢谢。

import java.util.*;
public class MyClass {
    public static void main(String args[]) {
       List<Integer> ints1 = new ArrayList<>(Arrays.asList(0,1,2));
       System.out.println(ints1);
       List<Integer> ints2 = ints1;
       ints2.set(0,0+10);
       System.out.println(ints1);
       List<Integer> ints3 = new ArrayList<>(ints1);
       ints3.set(1,1+10);
       System.out.println(ints1);
    }
}
result
[0, 1, 2]
[10, 1, 2]
[10, 1, 2]
Java 构造函数 copy-constructor deep-copy

评论

3赞 Jason 12/18/2017
不要将 Integer 的不可变性与浅拷贝与深拷贝混为一谈。读一读这个: yunmingzhang.wordpress.com/2014/01/08/...
1赞 DhaRmvEEr siNgh 12/18/2017
ints1 和 ints2 引用相同的对象,而 ints3 是一个单独的对象,它是使用 ints1 中的值创建的。因此,ints2 中的任何更改都会更改 ints1。但是 ints3 中的更改不会影响其他两个对象。.如果您感到困惑,请尝试打印所有对象的哈希码,然后您可以看到差异。

答:

0赞 Johan Witters 12/18/2017 #1

答案是:浅拷贝。看看这篇文章,它提供了有关该主题的更多详细信息:http://javarevisited.blogspot.co.uk/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html?m=1

0赞 Shubham 8/14/2018 #2

问题的关键在于,当您这样做时,您实际上是在更改存储在 中的引用。存储在 中的引用不受此影响。ints3.set(1, 1+10)ints3[1]ints1[1]

// Format

 ---------- 
|  Value   |
 ---------- 
  Address


// Actual data
------       ------      -------            ------
|  1   | ...|   2  | ... |  3  |   ...... |  42   |  
------       ------      -------            ------
   10          20           30                80


// ArrayList <Integer> a = new ArrayList <Integer> (Arrays.asList(1, 2, 3));
------        ------ ------ ------
| 100 | ---> |  10  |  20  |  30  |
------        ------ ------ ------
  a             100    104    108


// ArrayList <Integer> b = new ArrayList <Integer> (a);
------        ------ ------ ------
| 200 | ---> |  10  |  20  |  30  |
------        ------ ------ ------
  b             200    204    208


When you do:

a[1] = 42, it is equivalent to:

------        ------ ------ ------
| 100 | ---> |  10  |  80  |  30  |
------        ------ ------ ------
  a             100    104    108


Note that this does not change b in any way.

另外,请查看以下代码。在这里,您可以看到它确实是浅拷贝,因为当您修改引用指向的值时,它会同时反映在 和 中。ab

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

class Node {

    int data;
    String name;

    Node(int data, String name) {

        this.data = data;
        this.name = name;
    }

    public String toString() {

        return "{" + Integer.toString(data) + ", " + name + "}";
    }
}

class ArrayListTest {

    public static void main(String[] args) {

        ArrayList <Node> a = new ArrayList <Node>();

        Node n[] = new Node[5];
        for(int i = 0; i < 5; i++) {
            n[i] = new Node(i, Integer.toString(i));
        }

        a.add(n[0]);
        a.add(n[1]);
        a.add(n[2]);

        System.out.println("Previously: ");
        System.out.println("a = " + a.toString());

        ArrayList <Node> b = new ArrayList <Node> (a);

        System.out.println("b = " + b.toString());

        // This does not affect b as b has aready made a copy of 
        // all the references in a different location.
        a.add(n[3]);

        n[2].data = 10;
        n[2].name = "10";

        System.out.println("\nCurrent: ");
        System.out.println("a = " + a.toString());
        System.out.println("b = " + b.toString());

    }
}

评论

0赞 Arjun 3/24/2019
所以基本上当你创建一个克隆时,使用复制构造函数,它只是创建一个浅拷贝,但是当你修改对象的内容时,它会在不同的内存位置创建一个深拷贝?我的理解正确吗?
0赞 Shubham 3/26/2019
@Arjun 克隆会创建一个浅拷贝。修改对象的内容不会在内存中创建新对象。就像在上面的代码示例中一样,我能够修改 的内容,并且它反映在两个 ArrayLists 中,因为我没有更改对存储在 ArrayLists 中的对象的引用。但是,如果我仅修改它,则存储在 ArrayList 中的引用将更改,但不受影响。n[2]a.set(1, n[0])ab
0赞 Arjun 3/28/2019
嗯,这就是我要问的。如果 ArrayList 像你说的那样被修改,那么存储在中的引用就会改变,这是否意味着在新的内存位置生成了一个独立的 ArrayList w.r.t?即深度副本?a.set(1, n[0])ab
0赞 Shubham 4/1/2019
@Arjun不会生成与 相关的新列表。b
0赞 Arjun 4/1/2019
我还是不明白。如果没有生成新的列表,那么为什么当列表被修改时,列表仍然不受影响? 的哈希码也发生了变化,但哈希码保持不变。两者引用不同的内存位置和列表的内容也不同。如果没有生成新列表,那么这怎么可能呢?ababab