如何在Java中克隆通用列表?


155

我有一个ArrayList<String>我想退还的副本。 ArrayList有一个具有以下签名的克隆方法:

public Object clone()

调用此方法后,如何将返回的对象强制转换回ArrayList<String>


16
不,这是一个有效的问题。Java不支持带有运行时类型擦除和所有功能的“ true”泛型,因此这些类型的细节可能很棘手。此外,Cloneable接口和Object.clone()方法机制同样令人困惑。
Outlaw程序员

好的,我主要在C#上做这件事,这真的很容易。如果您要我删除此问题中的评论,请告诉我。
Espo

1
您可以发表评论。我认为我的修改说明了我遇到的麻烦。
比尔蜥蜴

2
如果有点屈尊,您的评论还可以。我想象Java开发人员必须克服的许多障碍对于.NET开发人员而言似乎很愚蠢。
Outlaw程序员

1
@Oscar,他想克隆,而不是调用clone命令。克隆实际上不是克隆的可能并不相同。我认为这是重点。这确实是一个棘手的问题。
拉法

Answers:


62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

43
这对于Strings来说可以很好地工作(这是问题的要求),但是值得注意的是ArrayList.clone将执行浅表复制,因此,如果列表中存在可变对象,则不会将其克隆(并更改一个)一个列表中的一个也将更改另一个列表中的一个
。– pkaeding

49
您应该避免在原始代码以外的任何内容中使用原始类型。最好使用ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();
cdmckay,2009年

20
太糟糕了,ArrayList具有#clone方法,但List本身没有。叹。
rogerdpack 2012年

12
不相关,但在使用时:使用List<String>而不是ArrayList<String>在左侧。这就是大多数情况下应该使用集合的方式。
Christophe Roussy 2013年

3
我无法使用此方法来复制ArrayList。无法识别clone()方法。
2014年

318

为什么要克隆?创建新列表通常更有意义。

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

任务完成。


17
您可能不知道列表的类型。它可能是LinkedList,MyOwnCustomList或ArrayList的子类,在这种情况下,更新ArrayList将是错误的类型。
史蒂夫郭

51
我是否愿意使用原始列表的哪个实现?我可能在乎新列表使用哪种实现。
汤姆·霍顿

12
@Steve Kuo:签名ArrayList(Collection<? extends E> c)意味着您使用哪种列表作为参数都无关紧要。
cdmckay,2009年

3
我的意思是,它不是完整的副本,是吗?

4
@YekhezkelYovel不,不是。
汤姆·霍汀-大头钉


19

使用Java 8可以将其克隆为流。

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());

可以像List <AnObject> xy = new ArrayList <>(oldList);一样完成。
mirzak

1
这不是深度复制,一个列表元素的更改可以在另一个列表中看到
Inchara

2
在问题的哪个地方指定需要深拷贝?假设需要另一个包含相同对象的集合。如果您想沿深层复制路线走,那么您将打开一个全新的蠕虫罐。
西蒙·詹金斯

虽然不是克隆人,是吗?从API文档中“无法保证返回列表的类型,可变性,可序列化性或线程安全性”。Euw,不要其中之一。toCollection可能是一个更好的选择。
Tom Hawtin-大头钉'19

16

请注意,Object.clone()存在一些主要问题,在大多数情况下,不建议使用它。请参阅Joshua Bloch撰写的“ Effective Java ”中的第11项,以获取完整答案。我相信您可以在原始类型数组上安全地使用Object.clone(),但除此之外,您还需要谨慎地正确使用和覆盖克隆。最好定义一个复制构造函数或一个静态工厂方法来根据您的语义显式克隆对象。


15

我认为这应该使用Collections API来解决问题:

注意:复制方法以线性时间运行。

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);

13
为什么不只使用new ArrayList <String>(oldList)?
cdmckay,2009年

4
我相信这将行不通,来自doc *目标列表必须至少与源列表一样长。如果更长,则目标列表中的其余元素不受影响。
格雷格·多姆詹

@GregDomjan并非我同意这是最好的方法,但这是一种方法。要解决您的问题,它是这样的:List <String> newList = new ArrayList <>(oldList.size());
nckbrz 2014年

1
不知道它是否与Java 8有关,但是即使指定了大小,仍然会收到异常IndexOutOfBoundsException:destination.size()<source.size():0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); 似乎使用copyList.addAll(original);是一个很好的选择
Gene Bo

@GeneBo没有指定大小。它指定容量,这是完全不同的事情。神秘int争论的乐趣。我不知道为什么你要使用这种晦涩的静态方法代替好旧的方法addAll
汤姆·霍顿

8

我发现使用addAll可以正常工作。

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

使用括号而不是泛型语法


5
这将适用于字符串,但不适用于可变对象。您也将要克隆它们。
jodonnell's

是的,他的问题是字符串。而且他有泛型的问题,在这种情况下,泛型真的不喜欢铸造。
Allain Lalonde

另外,ArrayList.clone只会进行浅表克隆,因此列表中的可变对象也不会使用该方法进行克隆。

那应该是ArrayList <String> btw。另外,您最好使用新的ArrayList <String>(原始),因为它的编写较少且清晰明了。
cdmckay,2009年

4
为什么不只是使用new ArrayList<String>(original)
user102008 2011年


4

要克隆一个通用接口,java.util.List只需要强制转换即可。这是一个例子:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

这有点棘手,但是如果您只能返回一个List接口,那么它就可以工作,因此任何人都可以在需要时实现列表。

我知道这个答案很接近最终答案,但是我的答案却回答了在与List-generic parent- 一起工作时如何做所有这些事情。ArrayList


2
您假设它将始终是一个不正确的ArrayList
jucardi 16-10-20

@JuanCarlosDiaz会问的,这是一个有关ArrayList的问题,所以我为ArrayList回答了:)
Ahmed Hamdy

2

克隆ArrayList时要非常小心。用Java克隆很浅。这意味着它将仅克隆Arraylist本身,而不克隆其成员。因此,如果您有一个ArrayList X1并将其克隆到X2中,则X2中的任何更改也将在X1中显示,反之亦然。克隆时,您将只生成一个新的ArrayList,该ArrayList带有指向原始元素中相同元素的指针。


2

这也应该起作用:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()

2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

请记住,这只是浅表而不是深表。您将获得一个新列表,但条目相同。对于简单的字符串,这没有问题。当列表条目本身就是对象时,变得更加棘手。


1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();

1

我不是Java专业人员,但是我有同样的问题,因此尝试通过这种方法解决。(假设T有一个复制构造函数)。

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}

不能保证List实现类具有公共的无参数构造函数。例如,Lists返回Array.asListList.of或者List.subList可能不是。
汤姆·霍顿-抢险
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.