ArrayList浅拷贝:迭代还是clone()?

23

我需要一个Java ArrayList的浅拷贝,我应该使用 clone() 还是迭代原始列表并将元素复制到新的 ArrayList 中,哪个更快?


1
在输入问题时,您应该已经看到了弹出的相关问题列表(与您在此页面右下角看到的相同列表)。您有没有翻看它们?为什么这些答案不足呢?请详细说明。 - BalusC
我已经查看了弹出窗口,但是没有任何关于ArrayList迭代器与clone()在性能方面的相关内容。 - tech20nn
4个回答

59

3
然而,这并不是线程安全的。(由于这个问题我最终到了这里) - Gubatron

14

使用 clone() 方法或使用复制构造函数来实现。

复制构造函数会将传递的集合转换为数组,并进行其他的一些变换,而 clone() 方法直接使用内部数组。

请注意,clone() 方法返回的是 Object,因此需要将其强制转换为 List


我确实查看了java.util.ArrayList源代码,并发现clone()使用了Array.copyof,这比循环原始ArrayList要高效得多。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(); } } - tech20nn
2
我不会太担心效率问题。使用clone()很麻烦,建议使用转换构造函数。 - Kevin Bourrillion
2
@Kevin Bourrillion,你为什么认为使用clone()很痛苦?实现clone()才是痛苦的,而不是使用它。 - Bozho
4
通常情况下,您不应编写太多依赖于实现类ArrayList的代码;通用的最佳实践是将依赖限制在诸如List之类的接口类型上。但是,在不知道可以向下转换为哪种实现类型(例如,ArrayListLinkedList等)之前,您无法克隆List!然后,一旦获得结果,它就是一个Object,因此还需要进行强制类型转换!最糟糕的是,某些实现不会忽略它们自己的CloneNotSupportedException。更糟糕的是,因为这非常麻烦(续)。 - Kevin Bourrillion
2
与其使用简单的转换构造函数,许多库根本不会费心玩复制游戏。社区大多已经对这一切感到厌烦。我建议您完全忘记克隆的概念,可能偶尔克隆一个数组是方便的。坦率地说,情况如此糟糕,以至于我很惊讶听到有人断言使用它不痛苦! - Kevin Bourrillion
@Kevin Bourrillion https://dev59.com/tXE85IYBdhLWcg3w02_z - Bozho

9

你可以使用复制构造函数来代替手动迭代。

至于使用clone()和使用复制构造函数之间的速度差异:

  1. 这没关系
  2. 很可能没有差别
  3. 针对您特定的系统配置和用例进行基准测试

@Michael..谢谢。我检查了复制构造函数的代码。它有额外的步骤来返回Bozho提到的复制内部数组结构。public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } - tech20nn
我喜欢通用的“我应该优化吗?”思维模式。 - Jan Šimbera

-3

问题中提到的是浅拷贝而不是深拷贝。直接从一个ArrayList引用复制到另一个引用也可以正常工作。深拷贝包括复制ArrayList中的每个元素。

ArrayList<Integer> list=new ArrayList<Integer>();
list.add(3);
ArrayList<Integer> list1=list; //shallow copy...

这里有什么问题吗?


3
这是错误的。 所有这样做只会使得内存中有两个指针指向同一个物理 ArrayList 对象。在一个列表中添加一个 Integer 也会让这个 Integer 在另一个列表中出现,而这并不是我们想要的结果。浅拷贝使底层对象共享相同的内存空间,但列表具有独立的内存空间。所以如果你修改一个 Integer,这个改变将会反映在两个列表中。但是如果你向一个列表中添加一个 Integer,它不会出现在另一个列表中。深拷贝不会在另一个列表中反映出任何的更改。 - Antimonit
list1 = list 只是将指向 ArrayList<Integer>(名为“list”)的指针分配给“list1”。原帖要求一个“list1”,其中包含“list”中包含的元素的浅拷贝。 - ONE

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