当前位置:  开发笔记 > 编程语言 > 正文

如何克隆ArrayList并克隆其内容?

如何解决《如何克隆ArrayList并克隆其内容?》经验,为你挑选了8个好方法。

如何克隆ArrayList并在Java中克隆其项目?

例如,我有:

ArrayList dogs = getDogs();
ArrayList clonedList = ....something to do with dogs....

我希望那些物品clonedList与狗列表中的物品不同.



1> Varkhan..:

您将需要迭代这些项目,并逐个克隆它们,然后将克隆放入结果数组中.

public static List cloneList(List list) {
    List clone = new ArrayList(list.size());
    for (Dog item : list) clone.add(item.clone());
    return clone;
}

为了实现这一点,显然,您必须让您的Dog类实现Cloneable接口并覆盖该clone()方法.


但是,你不能一般地做到这一点.clone()不是Cloneable接口的一部分.
但是clone()在Object中受到保护,因此您无法访问它.尝试编译该代码.
所有类都扩展了Object,因此它们可以覆盖clone().这就是Cloneable的用途!
我说,创建一个工厂或构建器,甚至只是一个静态方法,它将获取Dog的实例,并手动将字段复制到新实例,并返回该新实例.
这是一个很好的答案.Cloneable实际上是一个接口.但是,mmyers有一点,因为clone()方法是Object类中声明的受保护方法.您必须在Dog类中重写此方法,并自己手动复制字段.

2> cdmckay..:

我个人会给Dog添加一个构造函数:

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

然后迭代(如Varkhan的回答所示):

public static List cloneList(List dogList) {
    List clonedList = new ArrayList(dogList.size());
    for (Dog dog : dogList) {
        clonedList.add(new Dog(dog));
    }
    return clonedList;
}

我发现这样做的好处是你不需要在Java中使用破解的Cloneable东西.它还与您复制Java集合的方式相匹配.

另一种选择可能是编写自己的ICloneable接口并使用它.这样你就可以编写一个通用的克隆方法.


+1复制构造函数是要走的路.也像ICloneable接口的想法.

3> Rose Perrone..:

所有标准集合都有拷贝构造函数.使用它们.

List original = // some list
List copy = new ArrayList(original); //This does a shallow copy

clone()设计有几个错误(见这个问题),所以最好避免它.

从Effective Java 2nd Edition,第11项:明智地覆盖克隆

鉴于与Cloneable相关的所有问题,可以肯定地说其他接口不应该扩展它,并且为继承而设计的类(第17项)不应该实现它.由于它有许多缺点,一些专家程序员只是选择永远不要覆盖克隆方法,永远不要调用它,除非复制数组.如果您设计了一个继承类,请注意,如果您选择不提供行为良好的受保护克隆方法,则子类将无法实现Cloneable.

本书还介绍了复制构造函数相对于Cloneable/clone的许多优点.

他们不依赖于易于冒险的语言外对象创建机制

他们并不要求无法强制遵守精简文件的惯例

它们与正确使用最终字段不冲突

它们不会抛出不必要的已检查异常

他们不需要演员阵容.

考虑使用复制构造函数的另一个好处:假设您有一个HashSet s,并且您希望将其复制为TreeSet.克隆方法无法提供此功能,但转换构造函数很容易:new TreeSet(s).


就我所知,标准集合的副本构造函数创建了*浅*副本,而不是*深*副本.这里提出的问题寻找一个深刻的副本答案.
这完全错误,复制构造做了一个浅层复制 - 问题的唯一问题

4> Lii..:

Java 8提供了一种新方法,可以优雅紧凑地调用元素狗上的复制构造函数或克隆方法:Streams,lambdascollectors.

复制构造函数:

List clonedDogs = dogs.stream().map(Dog::new).collect(toList());

该表达式Dog::new称为方法引用.它创建了一个函数对象,该对象调用构造函数,在Dog该构造函数上将另一个狗作为参

克隆方法[1]:

List clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());

得到一个ArrayList结果

或者,如果你需要ArrayList退回(如果你想稍后修改它):

ArrayList clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));

更新列表

如果您不需要保留dogs列表的原始内容,则可以使用该replaceAll方法并更新列表:

dogs.replaceAll(Dog::new);

所有例子都假设import static java.util.stream.Collectors.*;.


收藏家的ArrayLists

可以将最后一个示例中的收集器制成util方法.因为这是一件很常见的事情,我个人喜欢它简短而漂亮.像这样:

ArrayList clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());

public static  Collector> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

[1]注意事项CloneNotSupportedException:

对于此解决方案的工作clone方法,Dog 必须不要声明它抛出CloneNotSupportedException.原因是参数to map不允许抛出任何已检查的异常.

像这样:

    // Note: Method is public and returns Dog, not Object
    @Override
    public Dog clone() /* Note: No throws clause here */ { ...

然而,这应该不是一个大问题,因为无论如何这是最好的做法.(例如,Effectice Java提供了这个建议.)

感谢Gustavo注意到这一点.


PS:

如果你发现它更漂亮,你可以使用方法引用语法来完成同样的事情:

List clonedDogs = dogs.stream().map(Dog::clone).collect(toList());



5> javatar..:

基本上有三种方法没有手动迭代,

1使用构造函数

ArrayList dogs = getDogs();
ArrayList clonedList = new ArrayList(dogs);

2使用 addAll(Collection c)

ArrayList dogs = getDogs();
ArrayList clonedList = new ArrayList();
clonedList.addAll(dogs);

3使用addAll(int index, Collection c)int参数的方法

ArrayList dogs = getDogs();
ArrayList clonedList = new ArrayList();
clonedList.addAll(0, dogs);

注意:如果在操作过程中修改了指定的集合,则将不确定这些操作的行为.


请注意,并非所有这三种变体都只创建列表的*浅拷贝*
这不是深度克隆,这两个列表保留相同的对象,只复制引用但只有Dog对象,一旦修改了列表,下一个列表将有相同的更改.不要这么多赞成.

6> Cojones..:

我认为目前的绿色答案很糟糕,为什么你会问?

它可能需要添加大量代码

它要求您列出要复制的所有列表并执行此操作

序列化的方式也很糟糕,你可能需要在整个地方添加Serializable.

那么解决方案是什么:

Java深度 克隆库克隆库是一个小型的开源(apache许可证)java库,它深入克隆对象.对象不必实现Cloneable接口.实际上,这个库可以克隆任何java对象.如果您不希望修改缓存对象或者只是想要创建对象的深层副本,则可以在缓存实现中使用它.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

请访问https://github.com/kostaskougios/cloning查看


这种方法的一个警告是它使用反射,这可能比Varkhan的解决方案慢得多.
我不明白第一点"它需要大量的代码".您正在谈论的库需要更多代码.它只是你放置它的地方的问题.否则我同意一个特殊的图书馆这种事情有帮助..

7> sagits..:

我找到了一种方法,您可以使用json对列表进行序列化/反序列化。未序列化时,序列化列表不包含对原始对象的引用。

使用gson:

List originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List newList = new Gson().fromJson(listAsJson, new TypeToken>() {}.getType());

您也可以使用jackson和其他任何json库来实现。



8> 小智..:

我一直使用此选项:

ArrayList clonedList = new ArrayList(name_of_arraylist_that_you_need_to_Clone);


嗯,我不知道为什么没人支持它,但是它有效。谢谢哥们儿!
推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有