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()
做了一个浅层副本。因此,b
和a
应指向相同的内存位置。但是,当我执行b.toString()
时,答案仅为5
。如果6
执行浅拷贝,为什么clone()
也不会显示?
答案 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不同,如果我们使用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]