clone():ArrayList.clone()我以为是浅拷贝
ArrayList a=new ArrayList(); a.add(5); ArrayList b=(ArrayList)a.clone(); a.add(6); System.out.println(b.toString());
在上面的代码中,我认为clone()
执行浅拷贝。 所以, b
和a
应该指向相同的内存位置。 但是,当我执行b.toString()
,答案只有5
。 如果clone()
执行浅拷贝,为什么6
也不显示?
浅拷贝并不意味着它们指向相同的内存位置。 那只是一个任务: List b = a;
。
克隆创建一个新实例, 包含相同的元素。 这意味着您有2个不同的列表,但它们的内容是相同的。 如果更改第一个列表中对象的状态,它将在第二个列表中更改。 (因为你使用的是不可变类型 – Integer
– 你无法观察到这一点)
但是,您应该考虑不使用clone()
。 它适用于集合,但通常它被认为是破碎的。 使用copy-constructors – new ArrayList(originalList)
如果它像你想的那样,那么clone
方法就完全没用了,因为在这种情况下,以下几行是等价的:
ArrayList b = (ArrayList )a.clone(); ArrayList b = a;
克隆是 – 就像在现实世界中的场景一样 – 创建具有完全相同属性的两个实体的过程(在克隆操作时)。
正如Bozho所提到的 – 避免使用Java clone()
概念。 即便是作者提到,它也被打破了。
这个问题和它的答案非常有价值,并提供了一个链接Josh Blochs自己对他的工作的评论;-)
浅层克隆是您正在讨论的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克隆和克隆类型(浅层和深层)
我们不能动态选择我们想要添加字符串的位置
int r=k.nextInt(); Integer i6=new Integer(r); System.out.println("Enter the address"); String p6=k.nextLine(); ar3.add(i6,p6);
它在读完整数后并没有排除它
Arraylist中的clonefunction与将一个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); }
输出: –
[一个]
[一个]
[]
[一个]
这确实是一个浅表副本,这里是来自ArrayList源代码的clone的注释
返回此ArrayList实例的浅表副本。 (元素本身不会被复制。)
为了理解这一点,让我们看看ArrayList中克隆方法的一个片段
v.elementData = Arrays.copyOf(elementData, size);
众所周知,当我们将一个Object分配给一个变量时,JAVA不会创建该Object的全新副本。 相反,此变量成为指向原始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 tt = new ArrayList (); tt.add(p1); tt.add(p2); ArrayList yy = (ArrayList ) 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中。