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

例如,我有:

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

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


当前回答

您需要手动克隆数组列表(通过迭代数组列表并将每个元素复制到一个新的数组列表中),因为clone()不会为您做这件事。原因是ArrayList中包含的对象本身可能无法实现Clonable。

编辑:…而这正是Varkhan的代码所做的。

其他回答

其他的海报是正确的:你需要迭代列表并复制到一个新的列表。

然而…… 如果列表中的对象是不可变的-你不需要克隆它们。如果你的对象有一个复杂的对象图,它们也需要是不可变的。

不可变性的另一个好处是它们也是线程安全的。

我个人会给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接口并使用它。这样就可以为克隆编写一个泛型方法。

您可以使用JSON(带有JSON库)来序列化和反序列化列表。序列化后的列表在未序列化时不包含对原始对象的引用。

使用谷歌GSON:

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

你也可以使用其他JSON库,比如Jackson。

使用这种方法的优点是可以解决问题,而不必创建类、接口和克隆逻辑(如果对象内部有其他对象列表,克隆逻辑可能会很长)

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注意到这一点。

我一直使用这个选项:

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