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

问题

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

例如我有:

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

我希望clonedList中的对象与狗列表中的对象不同。


#1 热门回答(169 赞)

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

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

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

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

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

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

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


#2 热门回答(168 赞)

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

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

为了实现这一点,显然,你必须让Dog对象实现Cloneable接口和clone()方法。


#3 热门回答(119 赞)

所有标准集合都有复制构造函数。使用它们。

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

clone()设计有几个错误(见this question),所以最好避免它。

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

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

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

  • 他们不依赖于易于冒险的语言外对象创建机制
  • 他们并不要求无法强制遵守精简文件的惯例
  • 它们与正确使用最终字段不冲突
  • 他们不会抛出不必要的检查异常
  • 他们不需要演员阵容。

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