为什么Collections.copy仍然复制引用,它不克隆复制的对象?

时间:2014-02-01 14:11:48

标签: java

好的,我只想将List<String[]> list复制到List<String[]> list2。之后我会修改list2&amp;中的对象。我希望它不会影响任何对象int list

            String[] s={"1","2"};
            List<String[]> list=new ArrayList<String[]>();
            list.add(s);
            List<String[]> list2=new ArrayList<String[]>(list);
            Collections.copy(list2,list);

            list2.get(0)[1]="3";

            for (String[] strings : list) {
                System.out.println(Arrays.toString(strings));
            }

输出:[1, 3]

为什么我们改变list2&amp;它会影响list1吗? 怎么解决?

2 个答案:

答案 0 :(得分:3)

Collections.copy()不会执行深层复制。

它只是将元素从一个集合复制到另一个集合。它与ArrayList构造函数基本相同,因此您无需同时调用它们。

两个列表中的第一个元素仍然引用同一个对象。因此,在运行代码时,您将修改两个列表中的数组。您可以迭代所有元素,并在每个列表项上使用Arrays.copyOf

这样的事情:

private List<String[]> deepCopy(List<String[]> list) {
    List<String[]> copy = new ArrayList<String[]>(list.size());
    for (String[] element : list) {
        copy.add(Arrays.<String> copyOf(element, element.length));
    }
    return copy;
}

编辑: Java&lt; 1.6版本:

private List<String[]> deepCopy(List<String[]> list) {
    List<String[]> copy = new ArrayList<String[]>(list.size());
    for (String[] element : list) {
        String[] elementCopy = new String[element.length];
        System.arraycopy(element, 0, elementCopy, 0, element.length);
        copy.add(elementCopy);
    }
    return copy;
}

答案 1 :(得分:1)

您要求的是复制集合克隆它所拥有的所有对象。在您的情况下,您只有字符串列表。其他人已经为你提供了很好的答案。就像一张纸条。一般来说,这并不容易,因为没有通用的克隆对象的方法。因为集合中的每个对象都可以引用其他对象本身。这些对象可以进一步引用更多对象。所以这取决于你需要什么。

  • 您是否只需要克隆集合中的对象,而不是那些对象引用的对象? 这称为“浅层复制”

  • 或者您是否需要将所有对象复制到对象参考树中。这称为“深度复制”

  • 或者您可能需要介于两者之间。这意味着您只需要复制某些对象。

你看,没有金色的子弹可以解决所有问题。这就是为什么它没有在通用集合类中实现。在某些情况下,如果您正在处理打开的文件句柄或其他系统资源,则甚至可能无法进行克隆。

但是你可以做的是让你的类实现Cloneable接口。在clone()方法中,您可以调用super.clone(),它已经为您进行了浅层克隆。除此之外的一切都需要由您自己实施。然后,您只需要为集合中的每个对象调用clone()以创建克隆对象。

这是如何实现Cloneable:

class MyClass implements Cloneable {

    private int a;
    private int b;
    private MyClass c;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

调用clone()会给你一个MyClass对象的副本。这意味着 a b 将是副本以及对象 c 的引用。但只有参考,而不是对象c本身。如果您需要,您需要执行以下操作:

    @Override
    public Object clone() throws CloneNotSupportedException {
        MyClass clone = (MyClass)super.clone();
        clone.c = (MyClass2)c.clone();
        return clone;
    }

以下是浅层和深层复制以及Cloneable接口的更详细说明:

http://javapapers.com/core-java/java-clone-shallow-copy-and-deep-copy/