clone() 本身并没有损坏:它根据clone() 方法的规范很好地实现了。有问题的地方是这个规范本身。正如在链接问题中已经讨论过的那样,有几个缺点,即必须进行未经检查的演员表,如果您的 ArrayList 被覆盖会出现的潜在问题以及需要知道输入 List 实际上是ArrayList。对于需要 ArrayList 的任何代码,一个好的做法是接受任何 List(甚至是 Collection)实现:这样您的代码就会变得更加灵活。
正如已经指出的,有一个更好的选择:使用
ArrayList<Type> copy = new ArrayList<>(source);
它是通用的:它适用于任何源集合,结果将完全是ArrayList(不是派生类)。你不应该担心性能。根据实现,它大致相同。查看clone()方法代码:
public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) 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();
}
}
所以它做了一个浅层副本(由于浅层大小是恒定且小的,因此快速操作的复杂性),然后制作单个数组副本并替换 mod 计数。让我们检查一下构造函数:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
它调用原始集合的toArray,将其结果用作自身的内部数组并更新大小。它仅在原始集合从toArray 方法错误地返回类型化数组的情况下复制数组。如果输入集合也是ArrayList,会发生什么?检查ArrayList.toArray:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
看,这和clone()方法中的操作完全一样。因此,在这两种情况下(克隆和复制构造函数),您只有一个 Arrays.copyOf(elementData, size) 调用和较小的常量开销。