我如何克隆一个数组列表,也克隆其项目在Java?

例如,我有:

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

我希望clonedList中的对象与dogs list中的对象不一样。


当前回答

Java 8提供了一种优雅而简洁地调用元素狗的复制构造函数或克隆方法的新方法:流、lambdas和收集器。

拷贝构造函数:

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

表达式Dog::new被称为方法引用。它创建了一个函数对象,调用Dog上的构造函数,该构造函数以另一个Dog作为参数。

克隆方法[1]:

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

得到一个数组列表作为结果

或者,如果你必须得到一个数组列表(以防你以后想修改它):

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

及时更新列表

如果你不需要保留狗列表的原始内容,你可以使用replaceAll方法并在适当的地方更新列表:

dogs.replaceAll(Dog::new);

所有示例都假设导入静态java.util.stream.Collectors.*;


数组列表收集器

上一个例子中的收集器可以被做成一个util方法。因为这是一件很常见的事情,我个人喜欢它是短而漂亮的。是这样的:

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

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

[1] CloneNotSupportedException异常说明:

为了使这个解决方案工作,Dog的克隆方法不能声明它抛出CloneNotSupportedException。原因是map的参数不允许抛出任何受控异常。

是这样的:

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

然而,这应该不是一个大问题,因为这是最佳实践。(例如,effective Java给出了这个建议。)

感谢Gustavo注意到这一点。

其他回答

一种糟糕的方法是用反思来做这件事。这种方法对我很管用。

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}

下面的方法对我有用。

在Dog.java

public Class Dog{

private String a,b;

public Dog(){} //no args constructor

public Dog(Dog d){ // copy constructor
   this.a=d.a;
   this.b=d.b;
}

}

 -------------------------

 private List<Dog> createCopy(List<Dog> dogs) {
 List<Dog> newDogsList= new ArrayList<>();
 if (CollectionUtils.isNotEmpty(dogs)) {
 dogs.stream().forEach(dog-> newDogsList.add((Dog) SerializationUtils.clone(dog)));
 }
 return newDogsList;
 }

在这里,由createCopy方法创建的新列表是通过SerializationUtils.clone()创建的。 因此,对新列表所做的任何更改都不会影响原始列表

简单的方法是

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

我个人会给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中破坏可克隆的东西。它还与复制Java集合的方式相匹配。

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

我一直使用这个选项:

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);