如何制作一个ArrayList的分离副本?

31

可能是重复问题:
Java:如何克隆ArrayList但也克隆其项?

我有一个类似以下代码的示例程序:

ArrayList<Invoice> orginalInvoice = new ArrayList<Invoice>();

//add some items into it here

ArrayList<Invoice> copiedInvoice = new ArrayList<Invoice>();

copiedInvoice.addAll(orginalInvoice);


我曾经认为我可以修改copiedInvoice中的项目而不会影响到originalInoice中的项目。但我错了。

我该如何创建一个独立的ArrayList副本/克隆?

谢谢


可能是重复的问题:https://dev59.com/kXRB5IYBdhLWcg3wHkPi,正如Zed所提到的。 - Philippe Carriere
1
我将此标记为重复,但不应删除。并非每个人都知道使用原始术语中更技术上正确的“克隆”术语进行搜索。 - Bill the Lizard
4个回答

42

是的,这是正确的- 你需要实现clone()(或另一种适合复制对象的机制,因为clone()在许多程序员看来是“有缺陷”的)。你的clone()方法应该对对象内所有可变字段进行深度复制。这样,对克隆对象的修改不会影响原始对象。

在你的示例代码中,你正在创建第二个ArrayList并将其填充指向相同对象的引用,这就是为什么从两个List都可以看到对象更改的原因。使用克隆方法,你的代码应该如下所示:

List<Foo> originalList = ...;

// Create new List with same capacity as original (for efficiency).
List<Foo> copy = new ArrayList<Foo>(originalList.size());

for (Foo foo: originalList) {
  copy.add((Foo)foo.clone());
}

编辑: 为了澄清,上述代码执行的是原始List深度复制,新的List包含指向原始对象副本的引用。这与调用ArrayList.clone()不同,后者执行List浅拷贝。在这个上下文中,浅拷贝创建一个新的List实例,但其中包含对原始对象的引用。


如果您深入探讨克隆列表与克隆列表中每个对象之间的区别,答案可能会更有帮助。然而,尽管没有解释,但该答案确实理解这一点,因此+1以弥补负分。 - Yishai
2
错误:在java.lang.object中,克隆具有“protected”访问权限。 - Bilal Rabbani
代码片段基于这样的假设:在类Foo中,clone()已经被重写为public。 - Adamski

22

如果您将可变对象存储到ArrayList中,当您复制ArrayList时,需要复制每个对象。否则,新的ArrayList仍将保留原始引用。

但是,如果您存储不可变对象,则可以使用:

ArrayList copiedInvoice = new ArrayList(originalInvoice);


3

我以为我可以修改复制的发票中的项目,而不会影响原始发票中的这些项目。

这是因为复制的是引用变量而不是对象本身。

因此,您最终得到了指向同一对象的两个“引用”。

如果您需要复制整个对象,则可能需要进行克隆。

但是,如果对象内部属性恰好是其他对象,则可能会遇到问题。

例如,以下类定义不会给您带来任何问题。

  public class Something {
       private int x;
       private int y;
       private String stringObject;
   }

如果你创建它的副本,你会复制其属性的当前值,仅此而已。

但是如果你的类内部有另一个对象,你可能也需要对其进行克隆。

 class OtherSomething {
        Something something;
       private int x;
 }

如果您执行以下操作:

 Something shared = new Something();

 OtherSomething one = new OtherSomething();

 OtherSomething two = new OtherSomething();

 one.something = shared;
 two.something = shared;

在这种情况下,变量one和变量two都引用了同一个共享的“something”,改变其中一个变量的值会影响另一个变量。

这就是为什么使用不可变对象要简单/更好/更容易。

如果需要更改不可变对象的值,只需创建一个新对象并赋予正确的值即可。


你说你不介意共享你的第一类:Something。然而,Date 是一个可变对象,因此需要进行复制。 - Adamski
糟糕的选择不可变对象.. 我本来想用可变对象的日期。谢谢Adamski。 - OscarRyz

-1

看一下ByteArrayOutputStream和ByteArrayInputStream。如果你的所有类都实现了Serializable接口,那么你可以使用上述类来进行复制。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接