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

例如,我有:

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

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


当前回答

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

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

其他回答

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

它可能需要添加大量代码 它要求你列出所有要复制的列表并这样做

序列化的方式在我看来也是不好的,你可能不得不到处添加Serializable。

那么解决方案是什么呢?

Java深度克隆库 克隆库是一个小型的开源(apache许可)java库,它对对象进行深度克隆。对象不必实现克隆接口。实际上,这个库可以克隆任何java对象。它可以用在缓存实现中,如果你不想修改缓存对象,或者当你想创建对象的深度副本时。

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

请登录https://github.com/kostaskougios/cloning查看

一些其他用于将ArrayList复制为深度复制的替代方法

Alernative 1 -使用外部包common -lang3,方法SerializationUtils.clone():

SerializationUtils.clone()

假设我们有一个类dog,其中类的字段是可变的,并且至少有一个字段是String类型和mutable类型的对象——而不是基本数据类型(否则浅拷贝就足够了)。

浅拷贝的例子:

List<Dog> dogs = getDogs(); // We assume it returns a list of Dogs
List<Dog> clonedDogs = new ArrayList<>(dogs);

现在回到狗的深度复制。

Dog类只有可变字段。

狗类:

public class Dog implements Serializable {
    private String name;
    private int age;

    public Dog() {
        // Class with only mutable fields!
        this.name = "NO_NAME";
        this.age = -1;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

注意,类Dog实现了Serializable!这使得可以使用方法“SerializationUtils.clone(dog)”

阅读main方法中的注释以理解结果。这表明我们已经成功地对ArrayList()进行了深度复制。看到 在“SerializationUtils.clone(dog)”下面:

public static void main(String[] args) {
    Dog dog1 = new Dog();
    dog1.setName("Buddy");
    dog1.setAge(1);

    Dog dog2 = new Dog();
    dog2.setName("Milo");
    dog2.setAge(2);

    List<Dog> dogs = new ArrayList<>(Arrays.asList(dog1,dog2));

    // Output: 'List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]'
    System.out.println("List dogs: " + dogs);

    // Let's clone and make a deep copy of the dogs' ArrayList with external package commons-lang3:
    List<Dog> clonedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).collect(Collectors.toList());
    // Output: 'Now list dogs are deep copied into list clonedDogs.'
    System.out.println("Now list dogs are deep copied into list clonedDogs.");

    // A change on dog1 or dog2 can not impact a deep copy.
    // Let's make a change on dog1 and dog2, and test this
    // statement.
    dog1.setName("Bella");
    dog1.setAge(3);
    dog2.setName("Molly");
    dog2.setAge(4);

    // The change is made on list dogs!
    // Output: 'List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]'
    System.out.println("List dogs after change: " + dogs);

    // There is no impact on list clonedDogs's inner objects after the deep copy.
    // The deep copy of list clonedDogs was successful!
    // If clonedDogs would be a shallow copy we would see the change on the field
    // "private String name", the change made in list dogs, when setting the names
    // Bella and Molly.
    // Output clonedDogs:
    // 'After change in list dogs, no impact/change in list clonedDogs:\n'
    // '[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]\n'
    System.out.println("After change in list dogs, no impact/change in list clonedDogs: \n" + clonedDogs);
}

输出:

List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
Now list dogs are deep copied into list clonedDogs.
List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]
After change in list dogs, no impact/change in list clonedDogs:
[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]

备注: 因为改变列表狗后对列表克隆狗没有影响/改变, 那么ArrayList的深度复制成功!

Alernative 2 -不使用外部包:

Dog类中引入了一个新方法“clone()”,与替代方案1相比,“implements Serializable”被删除了。

clone()

狗类:

public class Dog {
    private String name;
    private int age;

    public Dog() {
        // Class with only mutable fields!
        this.name = "NO_NAME";
        this.age = -1;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    /**
     * Returns a deep copy of the Dog
     * @return new instance of {@link Dog}
     */
    public Dog clone() {
        Dog newDog = new Dog();
        newDog.setName(this.name);
        newDog.setAge(this.age);
        return newDog;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

阅读下面主要方法中的评论以理解结果。这表明我们已经成功地对ArrayList()进行了深度复制。看到 下面是上下文中的“clone()”方法:

public static void main(String[] args) {
    Dog dog1 = new Dog();
    dog1.setName("Buddy");
    dog1.setAge(1);

    Dog dog2 = new Dog();
    dog2.setName("Milo");
    dog2.setAge(2);

    List<Dog> dogs = new ArrayList<>(Arrays.asList(dog1,dog2));

    // Output: 'List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]'
    System.out.println("List dogs: " + dogs);

    // Let's clone and make a deep copy of the dogs' ArrayList:
    List<Dog> clonedDogs = dogs.stream().map(dog -> dog.clone()).collect(Collectors.toList());
    // Output: 'Now list dogs are deep copied into list clonedDogs.'
    System.out.println("Now list dogs are deep copied into list clonedDogs.");

    // A change on dog1 or dog2 can not impact a deep copy.
    // Let's make a change on dog1 and dog2, and test this
    // statement.
    dog1.setName("Bella");
    dog1.setAge(3);
    dog2.setName("Molly");
    dog2.setAge(4);

    // The change is made on list dogs!
    // Output: 'List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]'
    System.out.println("List dogs after change: " + dogs);

    // There is no impact on list clonedDogs's inner objects after the deep copy.
    // The deep copy of list clonedDogs was successful!
    // If clonedDogs would be a shallow copy we would see the change on the field
    // "private String name", the change made in list dogs, when setting the names
    // Bella and Molly.
    // Output clonedDogs:
    // 'After change in list dogs, no impact/change in list clonedDogs:\n'
    // '[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]\n'
    System.out.println("After change in list dogs, no impact/change in list clonedDogs: \n" + clonedDogs);
}

输出:

List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
Now list dogs are deep copied into list clonedDogs.
List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]
After change in list dogs, no impact/change in list clonedDogs:
[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]

备注: 因为改变列表狗后对列表克隆狗没有影响/改变, 那么ArrayList的深度复制成功!

注一: 方案1比方案2慢得多, 但更容易维护,因为您不需要 更新任何方法,如clone()。

注2:对于替代方案1,以下maven依赖项用于方法“SerializationUtils.clone()””:

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>

更多common-lang3版本请访问:

https://mvnrepository.com/artifact/org.apache.commons/commons-lang3

您可以使用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。

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

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

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<>();
    }
}

下面是一个使用泛型模板类型的解决方案:

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}