如果 ArrayList 的 clone() 坏了,为什么他们不能修复它?
If clone( ) for ArrayList is broken why can't they fix it?
在阅读了多篇关于 ArrayList class 中的 clone( ) 如何被破坏的帖子 (here's one) 之后,我想知道没有人重新实现它以便它不会被破坏的地方人们可以更轻松地使用该方法。
我在这里遗漏了什么原因是没有人重新实现它以使其不被破坏吗?
clone()
本身并没有坏:它按照clone()
方法的规范很好地实现了。有问题的地方是这个规范本身。正如在链接问题中已经讨论过的那样,有几个缺点,即必须进行未经检查的转换,如果您的 ArrayList
被覆盖会出现潜在问题,并且必须知道输入 List
实际上是一个ArrayList
。对于任何需要 ArrayList
的代码,一个好的做法是接受任何 List
(甚至 Collection
)实现:这样您的代码就会变得更加灵活。
如前所述,有一个更好的选择:使用
ArrayList<Type> copy = new ArrayList<>(source);
它是通用的:它适用于任何源集合,结果将完全是 ArrayList
(不是派生的 class)。而且您不必担心性能。根据实施情况,它大致相同。查看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)
调用和较小的常量开销。
在阅读了多篇关于 ArrayList class 中的 clone( ) 如何被破坏的帖子 (here's one) 之后,我想知道没有人重新实现它以便它不会被破坏的地方人们可以更轻松地使用该方法。
我在这里遗漏了什么原因是没有人重新实现它以使其不被破坏吗?
clone()
本身并没有坏:它按照clone()
方法的规范很好地实现了。有问题的地方是这个规范本身。正如在链接问题中已经讨论过的那样,有几个缺点,即必须进行未经检查的转换,如果您的 ArrayList
被覆盖会出现潜在问题,并且必须知道输入 List
实际上是一个ArrayList
。对于任何需要 ArrayList
的代码,一个好的做法是接受任何 List
(甚至 Collection
)实现:这样您的代码就会变得更加灵活。
如前所述,有一个更好的选择:使用
ArrayList<Type> copy = new ArrayList<>(source);
它是通用的:它适用于任何源集合,结果将完全是 ArrayList
(不是派生的 class)。而且您不必担心性能。根据实施情况,它大致相同。查看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)
调用和较小的常量开销。