配列のコピー

みるくあいらんどっ! > ドキュメント > Java > じっくり学ぶ Java講座 [初心者向け・入門]


配列のコピー

ここでは、配列のコピーについて扱っていきます。ただ、配列のコピーそのものについて学ぶというよりは、配列のコピーについて学ぶことで、基本型と参照型+インスタンスの組み合わせとは決定的に違うことを理解していただければと思います。

以下では、配列をコピーし、コピー先の配列の中身を変更します。このとき、コピー元の配列に影響を与えない場合には成功、コピー元の配列が影響を受けてしまった場合には失敗として、基本型の配列と参照型の配列それぞれ 1例について実験していきます。

基本型配列のコピー

失敗例

最も簡単な記述で scores1を scores2にコピーしてみます。これで一見、コピーが成功したように見えますが、値を変更してみると実は失敗していることが分かります。

ソースコードは以下の通り。

J401/J401.java

/**
 * 基本型配列をコピーして、値を変更します(失敗例)。
 */
public class J401 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int[] scores1 = new int[] {70, 50, 80, 40, 90};
		int[] scores2 = new int[scores1.length];
		
		// コピー処理
		scores2 = scores1;
		
		// 値の表示
		System.out.println("変更前");
		J401.printArray("scores1", scores1);
		J401.printArray("scores2", scores2);
		
		// 値を変更
		scores2[2] = -100;
		
		// 値の表示
		System.out.println("変更後");
		J401.printArray("scores1", scores1);
		J401.printArray("scores2", scores2);
	}
	
	/**
	 * 配列の内容を表示します。
	 * @param name 名前
	 * @param array 配列
	 */
	private static void printArray(String name, int[] array) {
		System.out.print(name + ": ");
		for(int i = 0; i < array.length; i ++) {
			System.out.print(array[i] + " ");
		}
		System.out.println();
	}
}

実行結果は以下の通り。

変更前
scores1: 70 50 80 40 90 
scores2: 70 50 80 40 90 
変更後
scores1: 70 50 -100 40 90 
scores2: 70 50 -100 40 90 

scores2の参照先の配列インスタンスの内容を変えたのに、scores1の参照先の配列インスタンスも連動して変更されてしまっています。これでは失敗です。

イメージとしては以下のような感じです。String型インスタンスについては記述を省略しています。

基本型の失敗例

成功例

コピーの処理を変更してみました。

ソースコードは以下の通り。

J402/J402.java

/**
 * 基本型配列をコピーして、値を変更します(成功例)。
 */
public class J402 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int[] scores1 = new int[] {70, 50, 80, 40, 90};
		int[] scores2 = new int[scores1.length];
		
		// コピー処理
		for(int i = 0; i < scores1.length; i ++) {
			scores2[i] = scores1[i];
		}
		
		// 値の表示
		System.out.println("変更前");
		J402.printArray("scores1", scores1);
		J402.printArray("scores2", scores2);
		
		// 値を変更
		scores2[2] = -100;
		
		// 値の表示
		System.out.println("変更後");
		J402.printArray("scores1", scores1);
		J402.printArray("scores2", scores2);
	}
	
	/**
	 * 配列の内容を表示します。
	 * @param name 名前
	 * @param array 配列
	 */
	private static void printArray(String name, int[] array) {
		System.out.print(name + ": ");
		for(int i = 0; i < array.length; i ++) {
			System.out.print(array[i] + " ");
		}
		System.out.println();
	}
}

実行結果は以下の通り。

変更前
scores1: 70 50 80 40 90 
scores2: 70 50 80 40 90 
変更後
scores1: 70 50 80 40 90 
scores2: 70 50 -100 40 90 

scores2の値のみが変更されています。成功です。先ほどとの違いが分かるでしょうか、説明できるでしょうか。

イメージとしては以下のような感じです。String型インスタンスについては記述を省略しています。

基本型の成功例

参照型配列のコピー

失敗例 1

基本型と同じことを参照型でも実施してみます。まずはシンプルな記述による失敗例から。

ソースコードは以下の通り。

J403/J403.java

import java.awt.Point;

/**
 * 参照型配列をコピーして、値を変更します(失敗例1)。
 */
public class J403 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		Point[] points1 = {
				new Point(10, 15),
				new Point(100, 50),
				new Point(20, 30),
		};
		Point[] points2 = new Point[points1.length];
		
		// コピー処理
		points2 = points1;
		
		// 値の表示
		System.out.println("変更前");
		J403.printArray("points1", points1);
		J403.printArray("points2", points2);
		
		// 値を変更
		points2[1].x = -70;
		points2[1].y = -20;
		
		// 値の表示
		System.out.println("変更後");
		J403.printArray("points1", points1);
		J403.printArray("points2", points2);
	}
	
	/**
	 * 配列の内容を表示します。
	 * @param name 名前
	 * @param array 配列
	 */
	private static void printArray(String name, Point[] array) {
		System.out.print(name + ": ");
		for(int i = 0; i < array.length; i ++) {
			System.out.print("(" + array[i].x + "," + array[i].y + ") ");
		}
		System.out.println();
	}
}

実行結果は以下の通り。

変更前
points1: (10,15) (100,50) (20,30) 
points2: (10,15) (100,50) (20,30) 
変更後
points1: (10,15) (-70,-20) (20,30) 
points2: (10,15) (-70,-20) (20,30) 

points2の変更により points1が影響されてしまっています。

イメージとしては以下のような感じです。String型インスタンスについては記述を省略しています。

参照型の失敗例 1

失敗例 2

基本型のときに倣って、処理を変えてみます。しかし、これでも失敗します。

ソースコードは以下の通り。

J404/J404.java

import java.awt.Point;

/**
 * 参照型配列をコピーして、値を変更します(失敗例2)。
 */
public class J404 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		Point[] points1 = {
				new Point(10, 15),
				new Point(100, 50),
				new Point(20, 30),
		};
		Point[] points2 = new Point[points1.length];
		
		// コピー処理
		for(int i = 0; i < points1.length; i ++) {
			points2[i] = points1[i];
		}
		
		// 値の表示
		System.out.println("変更前");
		J404.printArray("points1", points1);
		J404.printArray("points2", points2);
		
		// 値を変更
		points2[1].x = -70;
		points2[1].y = -20;
		
		// 値の表示
		System.out.println("変更後");
		J404.printArray("points1", points1);
		J404.printArray("points2", points2);
	}
	
	/**
	 * 配列の内容を表示します。
	 * @param name 名前
	 * @param array 配列
	 */
	private static void printArray(String name, Point[] array) {
		System.out.print(name + ": ");
		for(int i = 0; i < array.length; i ++) {
			System.out.print("(" + array[i].x + "," + array[i].y + ") ");
		}
		System.out.println();
	}
}

実行結果は以下の通り。

変更前
points1: (10,15) (100,50) (20,30) 
points2: (10,15) (100,50) (20,30) 
変更後
points1: (10,15) (-70,-20) (20,30) 
points2: (10,15) (-70,-20) (20,30) 

基本型と異なり、参照型の場合にはこれでも失敗する理由が分かるでしょうか。冷静に考えれば、Point型の newは 3回しか行われていないため、Point型のインスタンスは 3個しかありません。

イメージとしては以下のような感じです。String型インスタンスについては記述を省略しています。

参照型の失敗例 2

成功例

上手く動作する例は以下のようになります。

ソースコードは以下の通り。

J405/J405.java

import java.awt.Point;

/**
 * 参照型配列をコピーして、値を変更します(成功例)。
 */
public class J405 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		Point[] points1 = {
				new Point(10, 15),
				new Point(100, 50),
				new Point(20, 30),
		};
		Point[] points2 = new Point[points1.length];
		
		// コピー処理
		for(int i = 0; i < points1.length; i ++) {
			points2[i] = new Point(points1[i].x, points1[i].y);
		}
		
		// 値の表示
		System.out.println("変更前");
		J405.printArray("points1", points1);
		J405.printArray("points2", points2);
		
		// 値を変更
		points2[1].x = -70;
		points2[1].y = -20;
		
		// 値の表示
		System.out.println("変更後");
		J405.printArray("points1", points1);
		J405.printArray("points2", points2);
	}
	
	/**
	 * 配列の内容を表示します。
	 * @param name 名前
	 * @param array 配列
	 */
	private static void printArray(String name, Point[] array) {
		System.out.print(name + ": ");
		for(int i = 0; i < array.length; i ++) {
			System.out.print("(" + array[i].x + "," + array[i].y + ") ");
		}
		System.out.println();
	}
}

実行結果は以下の通り。

変更前
points1: (10,15) (100,50) (20,30) 
points2: (10,15) (100,50) (20,30) 
変更後
points1: (10,15) (100,50) (20,30) 
points2: (10,15) (-70,-20) (20,30) 

成功しました。Point型のインスタンスは合計 6個作成されています。そのため、値を別々に格納することができます。

イメージとしては以下のような感じです。String型インスタンスについては記述を省略しています。

参照型の成功例

3つの例の違いが理解できたでしょうか。

最終更新: 2013/02/13 , 公開: 2013/02/13
▲top