概要
Javaにおける配列のコピーについて整理する。
- 1次元配列は
clone()
メソッドかSystem.arraycopy()
メソッド - 多次元配列は次元分forでネストして
clone()
かSystem.arraycopy()
Javaの配列は扱いづらく、deepToString()
やdeepEquals()
はあるのにdeepCopy()
がないのが不思議。
代入(assignment)
配列変数を他の変数に代入した場合、代入先の変数が元の変数と同じ配列を参照する。一方での変更が他方の結果にも反映される。
下記コードで、2つの配列のハッシュ値が同じこと、片方の変更が他方にも反映されることが確認できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import java.util.Arrays; public class ArrayCopy { public void assignment() { String[] array = {"A", "B", "C"}; String[] arrayAssigned = array; System.out.println(array); System.out.println(arrayAssigned); // [Ljava.lang.String;@7637f22 // [Ljava.lang.String;@7637f22 System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayAssigned)); // [A, B, C] // [A, B, C] array[0] = "X"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayAssigned)); // [X, B, C] // [X, B, C] arrayAssigned[1] = "Y"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayAssigned)); // [X, Y, C] // [X, Y, C] } public static void main(String[] args) { ArrayCopy app = new ArrayCopy(); app.assignment(); } } |
clone()
clone()
メソッドは配列の要素の「内容」を順次コピーして新たな配列を返す。一方の配列への変更は他方に影響しない。プリミティブ型の配列でも使える。
以下のコードで、2つの変数のハッシュ値が異なること、一方の変更が他方に影響していないことが確認できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import java.util.Arrays; public class ArrayCopy { public void cloning() { String[] array = {"A", "B", "C"}; String[] arrayCloned = array.clone(); System.out.println(array); System.out.println(arrayCloned); // [Ljava.lang.String;@7637f22 // [Ljava.lang.String;@3830f1c0 System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCloned)); // [A, B, C] // [A, B, C] array[0] = "X"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCloned)); // [X, B, C] // [A, B, C] arrayCloned[1] = "Y"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCloned)); // [X, B, C] // [A, Y, C] } public static void main(String[] args) { ArrayCopy app = new ArrayCopy(); app.cloning(); } } |
System.arraycopy()
System.arraycopy()
メソッドは引数にコピー元・コピー先の配列とコピー位置、コピー長さを指定して、コピー先にはコピー元の要素が複製された新たな配列が与えられる。clone()
と同じく、一方の配列への変更は他方に影響しない。
System.copy(元配列, 元開始位置, 先配列, 先開始位置, コピー長)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import java.util.Arrays; public class ArrayCopy { public void copy() { String[] array = {"A", "B", "C"}; String[] arrayCopied = new String[array.length]; System.arraycopy(array, 0, arrayCopied, 0, array.length); System.out.println(array); System.out.println(arrayCopied); // [Ljava.lang.String;@7637f22 // [Ljava.lang.String;@3830f1c0 System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCopied)); // [A, B, C] // [A, B, C] array[0] = "X"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCopied)); // [X, B, C] // [A, B, C] arrayCopied[1] = "Y"; System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCopied)); // [X, B, C] // [A, Y, C] } public static void main(String[] args) { ArrayCopy app = new ArrayCopy(); app.copy(); } } |
clone()はシャローコピー
多次元配列をclone()
でコピーしてもディープコピーにはならない。一方の配列への変更が他方に影響する。
以下のコードで、1次元目は違うハッシュ値でコピーされているが、それぞれが持っている要素配列はそのままのハッシュ値でコピーされている。
これは、clone()
で元の配列の要素(配列)が新たなインスタンスとして複製されるが、その要素は単に代入されるだけで再帰的に複製されないため。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import java.util.Arrays; public class ArrayCopy { public void multiDimShallowClone() { String[][] array = {{"A", "B"}, {"C", "D"}}; String[][] arrayCloned = array.clone(); System.out.println(array); System.out.println(arrayCloned); // [[Ljava.lang.String;@7637f22 // [[Ljava.lang.String;@3830f1c0 System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCloned)); // [[Ljava.lang.String;@39ed3c8d, [Ljava.lang.String;@71dac704] // [[Ljava.lang.String;@39ed3c8d, [Ljava.lang.String;@71dac704] System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[A, B], [C, D]] // [[A, B], [C, D]] array[0][0] = "X"; System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[X, B], [C, D]] // [[X, B], [C, D]] arrayCloned[1][0] = "Y"; System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[X, B], [Y, D]] // [[X, B], [Y, D]] } public static void main(String[] args) { ArrayCopy app = new ArrayCopy(); app.multiDimShallowClone(); } } |
多次元配列のディープコピー
多次元配列をディープコピーするには、最後の要素までclone()
で複製する必要がある。
2つの配列を同時に扱うのにPythonのzipのような機能がないので、カウンターで回さないといけない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import java.util.Arrays; public class ArrayCopy { public void multiDimDeepClone() { String[][] array = {{"A", "B"}, {"C", "D"}}; String[][] arrayCloned = new String[array.length][]; for (int i = 0; i < array.length; i++) { arrayCloned[i] = array[i].clone(); } System.out.println(array); System.out.println(arrayCloned); // [[Ljava.lang.String;@7637f22 // [[Ljava.lang.String;@3830f1c0 System.out.println(Arrays.toString(array)); System.out.println(Arrays.toString(arrayCloned)); // [[Ljava.lang.String;@39ed3c8d, [Ljava.lang.String;@71dac704] // [[Ljava.lang.String;@123772c4, [Ljava.lang.String;@2d363fb3] System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[A, B], [C, D]] // [[A, B], [C, D]] array[0][0] = "X"; System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[X, B], [C, D]] // [[A, B], [C, D]] arrayCloned[1][0] = "Y"; System.out.println(Arrays.deepToString(array)); System.out.println(Arrays.deepToString(arrayCloned)); // [[X, B], [C, D]] // [[A, B], [Y, D]] } public static void main(String[] args) { ArrayCopy app = new ArrayCopy(); app.multiDimDeepClone(); } } |