概要
配列の同一性は変数が指しているインスタンスが同じかどうかのみによるが、等価性の場合は末端の要素一つ一つが等価かどうかを判定することになる。
Javaの配列で等価性を判定する場合、1次元配列はArrays.equals()で等価性を判定できるが、2次元以上の配列になるとArrays.deepEquals()で判定しなければならない。
配列のコピーでもディープコピーができるメソッドがなくループで回さないといけないなど、Javaの配列は使いにくい。というよりも、下手に使うと思わぬところでミスの要因になりそう。
1次元配列
同一性(==)
配列変数が指している対象が同じかどうか。内容が同じでも変数が指しているインスタンスが異なる場合は同一にならない。
以下の例ではarray1とarray2が同じ配列インスタンスを指している(ハッシュ値が等しい)ので、内容が変わっても同一。
一方で変更前のarray1とarray4、変更後のarray1とarray3は、内容が同じでも配列のインスタンスが異なるため、同一とは判定されない。
|
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 |
public class ArrayEquality { String[] array1 = {"A", "B", "C"}; String[] array2 = array1; String[] array3 = {"X", "Y", "Z"}; String[] array4 = {"A", "B", "C"}; public void identity() { System.out.println(array1); // [Ljava.lang.String;@7637f22 System.out.println(array2); // [Ljava.lang.String;@7637f22 System.out.println(array3); // [Ljava.lang.String;@3830f1c0 System.out.println(array4); // [Ljava.lang.String;@39ed3c8d System.out.println(array1 == array2); // true System.out.println(array1 == array3); // false System.out.println(array1 == array4); // false array1[0] = "X"; array1[1] = "Y"; array1[2] = "Z"; System.out.println(array1 == array2); // true System.out.println(array1 == array3); // false System.out.println(array1 == array4); // false } public static void main(String[] args) { ArrayEquality app = new ArrayEquality(); app.identity(); } } |
等価性(Array.equals)
配列の内容が同じかどうか。異なる配列インスタンスでも内容が同じなら等価。同値性とも。
配列の等価性をチェックするにはArrays.equals()メソッドを使う。
以下の例では、array1とarray2は同一であり等価。array1の変更前はarray3と等価だが、変更後にはarray4と等価になる。
|
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 |
import java.util.Arrays; public class ArrayEquality { String[] array1 = {"A", "B", "C"}; String[] array2 = array1; String[] array3 = {"X", "Y", "Z"}; String[] array4 = {"A", "B", "C"}; public void arraysEquals() { System.out.println(Arrays.equals(array1, array2)); // true System.out.println(Arrays.equals(array1, array3)); // false System.out.println(Arrays.equals(array1, array4)); // true array1[0] = "X"; array1[1] = "Y"; array1[2] = "Z"; System.out.println(Arrays.equals(array1, array2)); // true System.out.println(Arrays.equals(array1, array3)); // true System.out.println(Arrays.equals(array1, array4)); // false } public static void main(String[] args) { ArrayEquality app = new ArrayEquality(); app.arraysEquals(); } } |
equals()メソッドは要注意
配列もオブジェクトとしてequals()メソッドを備えているが、これは同一性の判定しかせず、等価性の判定には使えない。
以下の例で、結果が==のときと同じになっているのが確認できる。
|
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 |
public class ArrayEquality { String[] array1 = {"A", "B", "C"}; String[] array2 = array1; String[] array3 = {"X", "Y", "Z"}; String[] array4 = {"A", "B", "C"}; public void equalsMethod() { System.out.println(array1.equals(array2)); // true System.out.println(array1.equals(array3)); // false System.out.println(array1.equals(array4)); // false array1[0] = "X"; array1[1] = "Y"; array1[2] = "Z"; System.out.println(array1.equals(array2)); // true System.out.println(array1.equals(array3)); // false System.out.println(array1.equals(array4)); // false } public static void main(String[] args) { ArrayEquality app = new ArrayEquality(); app.equalsMethod(); } } |
多次元配列
Array.equals()も要注意
Arrays.equals()はclone()の挙動と似ていて、
- 1次元目の各要素が同一なら結果は
true - 1次元目の各要素のいずれかが同一でないなら結果は
false
多次元配列でArray.equals()を使うと、2次元目の要素配列が同一であればtrueとなり、同一でなければ内容が同じでもfalseとなる。
以下の例では、
array2d1とarray2d2は元のインスタンスが同一であり等価array2d1からclone()で作られたarray2d3は、元のインスタンスは異なるが、それが指している要素が同じなので同一ではないが等価array2d3もarray2d4も、元のインスタンスから要素に至るまでarray2d1と異なるため、変更前/変更後に内容が同じになっても等価と判定されない
Arrays.equals()の等価性判定は1次元配列にしか使えない
|
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 |
import java.util.Arrays; public class ArrayEquality { String[][] array2d1 = {{"A", "B"}, {"C", "D"}}; String[][] array2d2 = array2d1; String[][] array2d3 = array2d1.clone(); String[][] array2d4 = {{"W", "X"}, {"Y", "Z"}}; String[][] array2d5 = {{"A", "B"}, {"C", "D"}}; public void arraysEquals2d() { System.out.println(array2d1 + " " + Arrays.toString(array2d1)); System.out.println(array2d2 + " " + Arrays.toString(array2d2)); System.out.println(array2d3 + " " + Arrays.toString(array2d3)); System.out.println(array2d4 + " " + Arrays.toString(array2d4)); System.out.println(array2d5 + " " + Arrays.toString(array2d5)); // [[Ljava.lang.String;@7637f22 [[Ljava.lang.String;@4926097b, [Ljava.lang.String;@762efe5d] // [[Ljava.lang.String;@7637f22 [[Ljava.lang.String;@4926097b, [Ljava.lang.String;@762efe5d] // [[Ljava.lang.String;@71dac704 [[Ljava.lang.String;@4926097b, [Ljava.lang.String;@762efe5d] // [[Ljava.lang.String;@123772c4 [[Ljava.lang.String;@2d363fb3, [Ljava.lang.String;@7d6f77cc] // [[Ljava.lang.String;@5aaa6d82 [[Ljava.lang.String;@73a28541, [Ljava.lang.String;@6f75e721] System.out.println(Arrays.equals(array2d1, array2d2)); // true System.out.println(Arrays.equals(array2d1, array2d3)); // true System.out.println(Arrays.equals(array2d1, array2d4)); // false System.out.println(Arrays.equals(array2d1, array2d5)); // false array2d1[0][0] = "W"; array2d1[0][1] = "X"; array2d1[1][0] = "Y"; array2d1[0][1] = "Z"; System.out.println(Arrays.equals(array2d1, array2d2)); // true System.out.println(Arrays.equals(array2d1, array2d3)); // true System.out.println(Arrays.equals(array2d1, array2d4)); // false System.out.println(Arrays.equals(array2d1, array2d5)); // false } public static void main(String[] args) { ArrayEquality app = new ArrayEquality(); app.arraysEquals2d(); } } |
深層比較(deepEquals)
Arrays.deepEquals()は、引数の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 |
import java.util.Arrays; public class ArrayEquality { String[][] array2d1 = {{"A", "B"}, {"C", "D"}}; String[][] array2d2 = array2d1; String[][] array2d3 = array2d1.clone(); String[][] array2d4 = {{"W", "X"}, {"Y", "Z"}}; String[][] array2d5 = {{"A", "B"}, {"C", "D"}}; public void arraysDeepEquals2d() { System.out.println(Arrays.deepEquals(array2d1, array2d2)); // true System.out.println(Arrays.deepEquals(array2d1, array2d3)); // true System.out.println(Arrays.deepEquals(array2d1, array2d4)); // false System.out.println(Arrays.deepEquals(array2d1, array2d5)); // true array2d1[0][0] = "W"; array2d1[0][1] = "X"; array2d1[1][0] = "Y"; array2d1[1][1] = "Z"; System.out.println(Arrays.deepEquals(array2d1, array2d2)); // true System.out.println(Arrays.deepEquals(array2d1, array2d3)); // true System.out.println(Arrays.deepEquals(array2d1, array2d4)); // true System.out.println(Arrays.deepEquals(array2d1, array2d5)); // false } public static void main(String[] args) { ArrayEquality app = new ArrayEquality(); app.arraysDeepEquals2d(); } } |