Java - ArrayListディープコピー(deep copy)

Javaから深いコピーにリストをコピーする方法を紹介します。

深いコピーと浅いコピーの違いは、浅いコピーでリストをコピーしたときは、既存のリストの要素を変更したときにコピーの要素も変更されます。 元の変更がコピーに影響を与えないようにするには、深いコピーを使用してオブジェクト全体をコピーする必要があります。

1. クラスに clone() を実装して深いコピー

リストに格納される要素のクラスに clone() が実装されていれば、 clone() を利用して深いコピーができます。

クラスに clone() メソッドを実装するには、以下のように Cloneable を実装して clone() 関数をオーバーライドすればよい。 clone() メソッドでは super.clone() でオブジェクトのレプリカを作成でき、必要に応じて本来のオブジェクトにいくつかの変数を直接生成して割り当てることもできます。

public static class Student implements Cloneable {
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Student { name: " + name + ", age: " + age + " }";
    }
}

次の例は、Studentリストを深くコピーし、元のリストの要素が変更されたときにコピーされたリストの要素も一緒に変更されることを確認する例です。 結果を見ると、予想通り元リストの要素のみ変更されました。

import java.util.ArrayList;
import java.util.List;

public class Example {

    public static void main(String[] args) {

        List<Student> origList = new ArrayList<>();
        origList.add(new Student("John", 20));
        origList.add(new Student("Doe", 30));

        List<Student> deepCopyList = new ArrayList<>();
        for (Student student : origList) {
            deepCopyList.add((Student) student.clone());
        }

        Student first = origList.get(0);
        first.name = "Son";
        first.age = 32;

        System.out.println("origList: " + origList);
        System.out.println("deepCopyList: " + deepCopyList);
    }

    public static class Student implements Cloneable {
        public String name;
        public int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        @Override
        public String toString() {
            return "Student { name: " + name + ", age: " + age + " }";
        }
    }
}

Output:

origList: [Student { name: Son, age: 32 }, Student { name: Doe, age: 30 }]
deepCopyList: [Student { name: John, age: 20 }, Student { name: Doe, age: 30 }]

2. 浅いコピー(shallow copy)と深いコピー(deep copy)の違い

浅いコピーとは、元のリストの要素をコピーされたリストに追加するだけです。 つまり、リストのオブジェクトは異なるが元のリストとコピーリストに格納される要素は同じオブジェクトになります。

オブジェクトを深くコピーしないため、パフォーマンスとコストの面で利点があります。元のリストの要素が変更されると、コピーの要素も一緒に変わります。

以下の例は、浅いコピーにリストをコピーし、元の要素を変更したときに変更されることを確認する例です。 結果を見ると一緒に変わりました。

import java.util.ArrayList;
import java.util.List;

public class Example1 {

    public static void main(String[] args) throws CloneNotSupportedException {

        List<Student> origList = new ArrayList<>();
        origList.add(new Student("John", 20));
        origList.add(new Student("Doe", 30));

        List<Student> shallowCopyList = new ArrayList<>();
        for (Student student : origList) {
            shallowCopyList.add(student);
        }

        Student first = origList.get(0);
        first.name = "Son";
        first.age = 32;

        System.out.println("origList: " + origList);
        System.out.println("shallowCopyList: " + shallowCopyList);
    }

    public static class Student implements Cloneable {
        public String name;
        public int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        @Override
        public String toString() {
            return "Student { name: " + name + ", age: " + age + " }";
        }
    }
}

Output:

origList: [Student { name: Son, age: 32 }, Student { name: Doe, age: 30 }]
shallowCopyList: [Student { name: Son, age: 32 }, Student { name: Doe, age: 30 }]
codechachaCopyright ©2019 codechacha