clone():ArrayList.clone()我认为是一个浅拷贝

时间:2011-01-04 10:13:33

标签: java clone

ArrayList<Integer> a=new ArrayList<Integer>();
a.add(5);
ArrayList<Integer> b=(ArrayList<Integer>)a.clone();
a.add(6);
System.out.println(b.toString());

在上面的代码中,我认为clone()做了一个浅层副本。因此,ba应指向相同的内存位置。但是,当我执行b.toString()时,答案仅为5。如果6执行浅拷贝,为什么clone()也不会显示?

6 个答案:

答案 0 :(得分:49)

浅拷贝并不意味着它们指向相同的内存位置。这只是一项任务:List b = a;

克隆会创建一个实例,并保存相同的元素。这意味着您有2个不同的列表,但它们的内容是相同的。如果更改第一个列表中对象的状态,它将在第二个列表中更改。 (因为你使用的是不可变类型 - Integer - 你无法观察到这一点)

但是,您应该考虑不使用clone()。它适用于集合,但通常它被认为是破碎的。使用copy-constructors - new ArrayList(originalList)

答案 1 :(得分:6)

如果 就像你想的那样,那么clone方法完全没用,因为在这种情况下,以下行会< / em>等同于:

ArrayList<Integer> b = (ArrayList<Integer>)a.clone();
ArrayList<Integer> b = a;

克隆 - 就像在现实世界中的场景一样 - 创建具有完全相同属性的两个实体的过程(在克隆操作时)。

正如Bozho所说 - 避免使用Java clone()概念。即使它的作者提到,它也被打破了。

This question and it's answers非常有价值,并提供了Josh Blochs对他的工作评论的链接; - )

答案 2 :(得分:1)

这确实是一个浅拷贝,这里是来自ArrayList源代码的克隆的注释

  

返回此ArrayList实例的浅表副本。 (元素本身不会被复制。)

要理解这一点,让我们看看ArrayList

中克隆方法的片段
v.elementData = Arrays.copyOf(elementData, size);

众所周知,当我们将一个Object分配给一个变量时,JAVA并没有制作一个全新的副本 那个对象。相反,该变量成为指向原始Object的另一个引用。

因此,elementData实际上存储对放入此ArrayList的对象的引用。克隆只是 复制这些引用,不会创建对象的副本。

当然,您可以删除或添加对克隆ArrayList的新引用。

但是,修改一个ArrayList中的旧对象将影响原始ArrayList。因为Integer是不可变的,所以很难用你的例子做插图。

要查看副作用,您可以定义自定义可变对象

class Person {
        private int a;

        public void setA(int a) {
            this.a = a;
        }
        public int getA() {
            return a;
        }
        @Override
        public String toString() {
            return String.valueOf(a);
        } 
   } 

然后您可以使用以下代码进行测试

        Person p1 = new Person();
        Person p2 = new Person();

        ArrayList<Person> tt = new ArrayList<Person>();
        tt.add(p1);
        tt.add(p2);

        ArrayList<Person> yy = (ArrayList<Person>) tt.clone();
        Person vv = yy.get(yy.indexOf(p2));
        vv.setA(12);
        yy.remove(p1);

        System.out.println("tt: " + tt);
        System.out.println("yy: " +yy);

输出应为

  

tt:[0,12]
   yy:[12]

看到副作用:)?我们只改变yy中的元素,但它也反映在tt。

答案 3 :(得分:1)

浅层克隆是您正在讨论的Object.clone()提供的默认克隆策略。 对象类的clone()方法创建一个新实例,并将Cloneable对象的所有字段复制到该新实例(它是原始实例或引用)。因此,在引用类型的情况下,只有引用位被复制到新实例,因此,两个对象的引用变量将指向同一个对象。我们上面看到的例子是Shallow Cloning的一个例子。

深度克隆顾名思义,深度克隆意味着克隆从一个对象到另一个对象的所有内容。为实现这一目标,我们需要欺骗我们的clone()方法来提供我们自己的克隆策略。我们可以通过在对象层次结构中的每个引用类型中实现Cloneable接口和覆盖clone()方法,然后在对象的clone方法中调用super.clone()和这些clone()方法来实现。

但是如果你看一下源代码中ArrayList的clone()方法,你会看到它在调用v.elementData = Arrays.copyOf(elementData, size);之后外部复制super.clone(),这意味着ArrayList的clone()会深深地复制它含量

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

要了解有关克隆及其类型(如深度克隆和浅层克隆)的更多信息,请阅读Java Cloning and Types of Cloning (Shallow and Deep) in Details with Example

答案 4 :(得分:0)

我们不能动态选择我们想要添加字符串的位置

int r=k.nextInt();
Integer i6=new Integer(r);
System.out.println("Enter the address");
String p6=k.nextLine();
ar3.add(i6,p6);

在读完整数

之后并没有排除它

答案 5 :(得分:0)

Arraylist中的

克隆函数与将一个arraylist复制到另一个arraylist不同,如果我们使用clone(),它会保留原始arraylist的副本,但如果我们在使用clone()之后对原始arraylist进行任何更改,它将会不影响复制的arraylist .. 例如:

public static void main(String[] a) {

List list = new ArrayList();

list.add("A");

List list2 = ((List) ((ArrayList) list).clone());

System.out.println(list);
System.out.println(list2);

list.clear();

System.out.println(list);
System.out.println(list2);
}

输出: -

[A]

[A]

[]

[A]