我有一个类,它在其构造函数中获取对象列表,List<Object>
. 每次列表都可以由不同类型的元素组成。这是通用的,我不知道他们的班级类型是什么。
在让用户更改其值之前,我想为自己保存一份该列表的副本。但由于副本是通过引用完成的,因此两个列表(原件和副本)都在更改......
如何按值复制我的列表?
谢谢,德沃拉
我有一个类,它在其构造函数中获取对象列表,List<Object>
. 每次列表都可以由不同类型的元素组成。这是通用的,我不知道他们的班级类型是什么。
在让用户更改其值之前,我想为自己保存一份该列表的副本。但由于副本是通过引用完成的,因此两个列表(原件和副本)都在更改......
如何按值复制我的列表?
谢谢,德沃拉
你的问题不清楚。
如果您想要一个浅表副本(即包含对原始列表中对象的引用的列表副本,那么该clone
方法将完成这项工作,List
实现类上的复制构造函数也是如此。
如果您想要一个深层副本(即包含原始对象副本的列表副本),那么您最好的选择是创建一个新列表并使用原始列表元素的克隆填充它。但是,有一个问题。clone
默认不提供该方法。许多常见的实用程序类是可克隆的,自定义类不是......除非你已经实现了它。如果您的列表包含任何不可克隆的对象,您将在运行时遇到异常。
有其他深度复制替代方法clone
,但就像克隆一样,它们不适用于所有类。实际上,如果您的解决方案必须是通用的,这就是问题的症结所在。
您需要创建一个新List
列表,然后将原始列表的每个元素的克隆添加到其中。
List<Object> copy = new ArrayList<Object>(inputList.size());
for (Object item : inputList) {
copy.add(item.clone());
}
显然,您需要检查列表中的对象是否正确,Cloneable
否则它们将无法正确复制。
在这些类型的情况下,有三种常见的解决方案:
(1) 如果您知道各个元素具有可以按预期工作的clone()方法,请创建一个新数组并用每个相应元素的 clone() 填充它。根据经验,来自 JDK 的“基本”对象,例如字符串、数字表示等应该没问题。clone() 的一个缺点是它有点“松散的大炮”:如果该方法已被特定类覆盖,您不知道“克隆”到底有多深,以及哪些引用可能会得到是否重复使用。它还具有一个特殊的危险,即依赖于构造函数中的逻辑的可克隆对象的子类可能无法工作。但正如我所说,简单的 JDK 对象应该没问题。
(2)将列表序列化为包裹在 ByteArrayOutputStream 周围的 ObjectOutputStream。然后获取生成的字节数组,并在其周围包装 ByteArrayInputStream 和 ObjectInputStream 并从中读取列表的新的深层副本。
(3) 在理想世界中,创建一个新列表,用明确创建的相关对象的“副本”填充它。在代码不太繁琐的情况下,这是理想的解决方案,因为您“知道实际发生了什么”:在意外重新引用而不是重新创建对象方面不会有隐藏的惊喜,因为例如 clone() 方法没有像你预期的那样工作。
由于您有一个简单的容器对象(一个数组列表),您可以轻松地重新创建其结构,因此(2)可能是矫枉过正。它可以方便地重新创建更复杂的结构。
关于(1),您应该特别怀疑从第三方库 中克隆对象,因为这些对象不是专门为 clone() 设计的。问题在于,人们很容易将 JDK(可克隆)类或令牌 clone() 方法子类化,而无需真正详细考虑它——根据我的经验,clone() 有一些许多开发人员没有的微妙之处意识到(例如,构造函数不会在新对象上被调用),这可能会导致意想不到的问题。因此,如果您实际上无法看到可靠的源代码来确保 clone() 是安全的,我会避免使用它。
遍历传递的列表并克隆其内容以创建一个新列表,然后可以在不更改原始列表的情况下使用该列表。
private List<Object> clone;
public Foo(List<Object> original)
{
this.clone = new ArrayList<Object>(list.size());
for(Object item: original)
{
clone.add(item.clone());
}
}
如果您的对象不包含其他对象的任何其他引用,则可以使用 Object.clone(),但如果不是这样,则需要进行深度克隆。
Collections.copy(b,a);
如果你有:
List<Object> objects
您可以通过以下方式复制此列表:
List<Object> newObjects = new ArrayList<Object>(objects);
[更新:添加 ArrayList 的真实实现以证明它复制了新对象]
请查看 ArrayList 的实际实现,它复制对象 - 不包含对同一对象的引用。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}