配列のコピー clone()

配列のコピーは日常的に行われる処理です。
通常、forループで要素をコピーしたり、clone()メソッドを使ったりします。さらに、arraycopy()を利用することも出来ます。
しかし、見た目が「コピー」であっても、コピーの実現方式にはシャローコピーとディープコピーの二種類あるので、それぞれどう実現されているかを知っておくと良いでしょう。

シャローコピー(shallow copy:奥行きの無いコピー)

配列の参照情報をコピーします。コピーで作成した配列変数はコピー元の配列インスタンスを指し示しているので、コピー元の要素を変更しても、新しい配列の要素を変更しても、変更は両方に行われたと見えます。

ディープコピー(deep copy:突き詰めたコピー)

元の配列の内容を含めてコピーし新しい配列を作ります。コピーで作成した配列変数はコピー元の配列インスタンスではなく新しい配列インスタンスを指し示し、新しい配列インスタンスの中身はコピー元の配列の中身と同じものになっています。コピー元の要素を変更しても、新しい配列の要素を変更しても、変更が相手方に反映することはありません

新しく作った配列にforループで内容をコピーすれば、結果は当然ディープコピーになります。
一方、clone()メソッドで新しい配列を作成すると一般的にはシャローコピーになると考えられています。しかし、clone()メソッドはコピーする配列の内容によってシャロ―コピーにも、ディープコピーにもなるので注意が必要です。

配列要素が基本型の場合
int型の配列arrayOrg[]を作り、そのclone()メソッドでarrayNew[]を作ります。
まず、9行目で二つの配列変数の内容を比較し、配列変数が同じ配列インスタンスを指しているか否かを確認しています。
次にそれぞれの配列インスタンスの中身が同一化を確認。
その上で、19行目で新しい配列の要素の内容を変更し、その変更がコピー元の配列に影響するか確認し、影響していればシャローコピー、指定なければディープコピーとしています。

import java.util.*;

public class IntArrayClone {
	public static void main(String[] args) {
		
		int[] arrayOrg = { 10, 20 };
		int[] arrayNew = arrayOrg.clone();
		System.out.println("arrayOrg:" + arrayOrg + " arrayNew:" + arrayNew);
		if (arrayOrg == arrayNew) {
            System.out.println("arrayOrg == arrayNew");
        } else {
            System.out.println("arrayOrg != arrayNew");
        }
        if (Arrays.equals(arrayOrg, arrayNew)) {
            System.out.println("arrayOrg[] equals arrayNew[]");
        } else {
            System.out.println("arrayOrg[] not equals arrayNew[]");
        }
        arrayNew[1] = 10;
		System.out.println("\nchange arrayNew[1] = 10");
        for (int i = 0; i < arrayOrg.length; i++) {
            System.out.println("arrayOrg[" + i + "] = " + arrayOrg[i] + "   "
            				+ "arrayNew[" + i + "] = " + arrayNew[i]);
        }
		if(arrayNew[1] == arrayOrg[1]) System.out.println("shallow copy");
		else System.out.println("deep copy");
	}
}

実行結果は、新しい配列が指し示す配列インスタンスはコピー元の配列インスタンスとは異なりますが、配列の内容は同じ。そして、新しい配列の配列要素の内容を一部変えても、その影響はコピー元の配列には影響しない事を示しています。
従って、配列要素が基本型の場合のclone()メソッドはディープコピーで新しい配列を作ります

>java IntArrayClone
arrayOrg:[I@15db9742 arrayNew:[I@6d06d69c
arrayOrg != arrayNew
arrayOrg[] equals arrayNew[]

change arrayNew[1] = 10
arrayOrg[0] = 10   arrayNew[0] = 10
arrayOrg[1] = 20   arrayNew[1] = 10
deep copy

>

次に、配列要素がString型の場合
6行目で作成する配列をString型に変えて、同じ処理を行います。

import java.util.*;

public class StrArrayClone {
	public static void main(String[] args) {
		
		String[] arrayOrg = { "A", "B" };
		String[] arrayNew = arrayOrg.clone();
		System.out.println("arrayOrg:" + arrayOrg + " arrayNew:" + arrayNew);
		if (arrayOrg == arrayNew) {
            System.out.println("arrayOrg == arrayNew");
        } else {
            System.out.println("arrayOrg != arrayNew");
        }
        if (Arrays.equals(arrayOrg, arrayNew)) {
            System.out.println("arrayOrg[] equals arrayNew[]");
        } else {
            System.out.println("arrayOrg[] not equals arrayNew[]");
        }
        arrayNew[1] = "A";
		System.out.println("\nchange arrayNew[1] = A");
        for (int i = 0; i < arrayOrg.length; i++) {
            System.out.println("arrayOrg[" + i + "] = " + arrayOrg[i] + "   "
            				+ "arrayNew[" + i + "] = " + arrayNew[i]);
        }
		if(arrayNew[1].equals(arrayOrg[1])) System.out.println("shallow copy");
		else System.out.println("deep copy");
	}
}

結果は同じく、新しい配列が指し示す配列インスタンスはコピー元の配列インスタンスとは異なりますが、配列の内容は同じ。そして、新しい配列の配列要素の内容を一部変えても、その影響はコピー元の配列には影響しません。
従って、配列要素がString型の場合のclone()メソッドもディープコピーで新しい配列を作ります

>java StrArrayClone
arrayOrg:[Ljava.lang.String;@15db9742 arrayNew:[Ljava.lang.String;@6d06d69c
arrayOrg != arrayNew
arrayOrg[] equals arrayNew[]

change arrayNew[1] = A
arrayOrg[0] = A   arrayNew[0] = A
arrayOrg[1] = B   arrayNew[1] = A
deep copy

>

最後に、配列要素がオブジェクト型の場合です。
5行目から8行目で作成する配列をObjArrayというオブジェクト型に変えて、同じ処理を行います。

import java.util.*;

public class ObjArrayClone {
	public static void main(String[] args) {
		ObjArray[] arrayOrg = {
			new ObjArray(10),
			new ObjArray(20)
		};
		ObjArray[ ] arrayNew = arrayOrg.clone();
		System.out.println("arrayOrg:" + arrayOrg + " arrayNew:" + arrayNew);
		if (arrayOrg == arrayNew) {
            System.out.println("arrayOrg == arrayNew");
        } else {
            System.out.println("arrayOrg != arrayNew");
        }

        if (Arrays.equals(arrayOrg, arrayNew)) {
            System.out.println("arrayOrg[] equals arrayNew[]");
        } else {
            System.out.println("arrayOrg[] not equals arrayNew[]");
        }

        arrayNew[1].num = 10;
		System.out.println("\nchange arrayNew[1].num = 10");
        for (int i = 0; i < arrayOrg.length; i++) {
            System.out.println("arrayOrg[" + i + "] = " + arrayOrg[i] + "   "
            				+ "arrayNew[" + i + "] = " + arrayNew[i]);
        }
		if(arrayNew[1].equals(arrayOrg[1])) System.out.println("shallow copy");
		else System.out.println("deep copy");
	}
}

class ObjArray {
	int num;
	ObjArray(int num) {
		this.num = num;
	}
}

この場合も、新しい配列が指し示す配列インスタンスはコピー元の配列インスタンスとは異なりますが、配列の内容は同じ。しかし、新しい配列の配列要素の内容を一部変えると、その影響はコピー元の配列には影響してしまいます。
つまり、配列要素がオブジェクト型の場合のclone()メソッドは配列インスタンスをディープコピーで作成しますが、各配列要素が指し示すオブジェクトはシャロ―コピーで作成します

>java ObjArrayClone
arrayOrg:[LObjArray;@15db9742 arrayNew:[LObjArray;@6d06d69c
arrayOrg != arrayNew
arrayOrg[] equals arrayNew[]

change arrayNew[1].num = 10
arrayOrg[0] = ObjArray@7852e922   arrayNew[0] = ObjArray@7852e922
arrayOrg[1] = ObjArray@4e25154f   arrayNew[1] = ObjArray@4e25154f
shallow copy

>

この様に、同じclone()メソッドでもコピーする配列の内容によりコピーによって作られる配列の作りが変わるので、注意が必要です。

カテゴリー: Java タグ: , , , , パーマリンク