How to clone ArrayList and also clone its contents?

后端 未结 21 1915
小鲜肉
小鲜肉 2020-11-21 06:42

How can I clone an ArrayList and also clone its items in Java?

For example I have:

ArrayList dogs = getDogs();
ArrayList

        
相关标签:
21条回答
  • 2020-11-21 07:24

    Some other alternatives for copying ArrayList as a Deep Copy

    Alernative 1 - Use of external package commons-lang3, method SerializationUtils.clone():

    SerializationUtils.clone()
    

    Let's say we have a class dog where the fields of the class are mutable and at least one field is an object of type String and mutable - not a primitive data type (otherwise shallow copy would be enough).

    Example of shallow copy:

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

    Now back to deep copy of dogs.

    The Dog class does only have mutable fields.

    Dog class:

    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 +
                    '}';
        }
    }
    

    Note that the class Dog implements Serializable! This makes it possible to utilize method "SerializationUtils.clone(dog)"

    Read the comments in the main method to understand the outcome. It shows that we have successfully made a deep copy of ArrayList(). See below "SerializationUtils.clone(dog)" in context:

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

    Output:

    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}]
    

    Comment: Since there is no impact/change on list clonedDogs after changing list dogs, then deep copy of ArrayList is successful!

    Alernative 2 - Use of no external packages:

    A new method "clone()" is introduced in the Dog class and "implements Serializable" is removed compare to alternative 1.

    clone()
    

    Dog class:

    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 +
                    '}';
        }
    }
    

    Read the comments in the main method below to understand the outcome. It shows that we have successfully made a deep copy of ArrayList(). See below "clone()" method in context:

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

    Output:

    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}]
    

    Comment: Since there is no impact/change on list clonedDogs after changing list dogs, then deep copy of ArrayList is successful!

    Note1: Alternative 1 is much slower than Alternative 2, but easier to mainatain since you do not need to upadate any methods like clone().

    Note2: For alternative 1 the following maven dependency was used for method "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>
    

    Find more releases of common-lang3 at:

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

    0 讨论(0)
  • 2020-11-21 07:25

    I think the current green answer is bad , why you might ask?

    • It can require to add a lot of code
    • It requires you to list all Lists to be copied and do this

    The way serialization is also bad imo, you might have to add Serializable all over the place.

    So what is the solution:

    Java Deep-Cloning library The cloning library is a small, open source (apache licence) java library which deep-clones objects. The objects don't have to implement the Cloneable interface. Effectivelly, this library can clone ANY java objects. It can be used i.e. in cache implementations if you don't want the cached object to be modified or whenever you want to create a deep copy of objects.

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

    Check it out at https://github.com/kostaskougios/cloning

    0 讨论(0)
  • 2020-11-21 07:25

    I have just developed a lib that is able to clone an entity object and a java.util.List object. Just download the jar in https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U and use the static method cloneListObject(List list). This method not only clones the List but also all entity elements.

    0 讨论(0)
提交回复
热议问题