在Java中通过值复制对象列表

8
我有一个类,在它的构造函数中接收一个对象列表,List<Object>。每次列表可能由来自不同类型的元素组成。这是一种通用的方法,我不知道它们的类类型将是什么。
在允许用户更改值之前,我想保存该列表的副本。但是由于复制是通过引用进行的,因此两个列表(原始列表和副本)都会被更改...
如何按值复制我的列表?

请看:https://dev59.com/eHNA5IYBdhLWcg3wrf43 - DonCallisto
你的意思是说,如果在原始列表中添加/删除项目,你的新列表不应更改?还是你也想确保你的列表中的项目不能被修改? - assylias
为什么要复制任何东西?我已经编写Java程序25年了,从未复制过列表。重新思考。 - user207421
6个回答

2

你的问题不够清晰。

  • 如果你想要一个浅复制(即列表的副本将包含对原始列表中对象的引用),那么clone方法或者在List实现类上使用复制构造函数都可以完成任务。

  • 如果你想要一个深复制(即列表的副本包含原始对象的副本),那么最好的方法是创建一个新列表,并使用原始列表元素的克隆来填充它。但是,有一个问题。默认情况下不提供clone方法。许多常见的实用程序类都是可克隆的,但自定义类通常不是...除非你已经实现了它。如果你的列表包含任何不可克隆的对象,你将在运行时得到异常。

还有其他深度复制替代方案,但与克隆一样,它们不能适用于所有类。而且,如果你的解决方案必须是通用的,这真的是问题的关键。


1
你需要创建一个新的List,然后将原始列表的每个元素的克隆添加到其中。
List<Object> copy = new ArrayList<Object>(inputList.size());
for (Object item : inputList) {
   copy.add(item.clone());
}

很明显,你需要检查列表中的对象是否为 Cloneable,否则它们将不能正确地被复制。

对象没有克隆方法。 - Dvora
1
@Dvora 当然可以!http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#clone() - dogbane
请注意,clone()可能会成为问题的源头(有关详细信息,请参见我的答案)。 - Neil Coffey


0
如果您的对象不包含任何其他对象的引用,您可以使用Object.clone(),但如果不是这样,您需要进行深层克隆。

0
在这些情况下,有三种常见的解决方案:
(1)如果您知道各个元素具有按您意图工作的< strong>clone()方法,请创建一个新数组并用每个相应元素的克隆填充它。 作为经验法则,“基本”JDK对象,例如字符串、数字表示等应该没问题。 clone()的缺点是它有点像“松炮弹”:如果该方法已被覆盖以用于特定类,则事先不知道“克隆”的深度,可能会重新使用哪些引用或不重新使用哪些引用。 它还具有特定危险,即依赖其构造函数逻辑的可克隆对象的子类可能无法正常工作。 但是,正如我所说,简单的JDK对象应该没问题。
(2)将列表串行化为包装在ByteArrayOutputStream周围的ObjectOutputStream。 然后,取得结果字节数组,并在其周围包装ByteArrayInputStream和ObjectInputStream,并从中读取列表的新、深层副本。

(3)在理想情况下,创建一个新列表,并用明确创建的“副本”填充它。如果编码不太麻烦,这是理想的解决方案,因为你“知道实际发生了什么”:在对象意外重新引用而不是重新创建的情况下,不会有隐藏的意外惊喜,例如clone()方法没有按预期工作。

由于您拥有一个简单的容器对象(数组列表),其结构可以轻松重新创建,因此(2)可能过度。对于重新创建更复杂的结构,它可能很方便。

关于(1),您应该特别怀疑从第三方库中克隆对象,其中对象并非专门设计为考虑clone()。问题在于人们很容易从JDK(可克隆)类或者一个标记的clone()方法进行子类化,而不会详细考虑它——根据我的经验,clone()具有许多开发人员不知道的细微差别(例如构造函数不会在新对象上调用),这可能导致意想不到的问题。因此,如果您无法看到可靠的源代码以确保clone()是安全的,我建议避免使用它。


0

遍历传递的列表并复制其内容以创建一个新列表,然后可以使用该新列表而不更改原始列表。

private List<Object> clone;
public Foo(List<Object> original)
{
  this.clone = new ArrayList<Object>(list.size());
  for(Object item: original)
  { 
   clone.add(item.clone());
  }
}

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