Arrays.equals の役割をざっくりつかむ
java.util.Arrays.equals は、
「配列同士が “中身まで含めて” 同じかどうかをチェックするメソッド」
です。
ここでまず押さえたいのが、
a == b は「同じ配列オブジェクトかどうか(アドレス比較)」Arrays.equals(a, b) は「要素数・要素の値がすべて同じか(中身の比較)」
という違いです。
配列同士を比べるときに == を使うと、ほぼ確実に間違えます。
「中身が同じか見たい」なら、基本的に必ず Arrays.equals を使います。
まずは基本:int 配列同士の比較でイメージをつかむ
== だと false、Arrays.equals だと true になる例
次のコードを見てください。
import java.util.Arrays;
public class ArraysEqualsBasic {
public static void main(String[] args) {
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // 1
System.out.println(Arrays.equals(a, b)); // 2
}
}
Java多くの人が、コメントのところで「えっ」となります。
a == b はほぼ確実に false になります。a と b は別々に new された配列で、
メモリ上の「場所(参照)」が違うからです。
一方で Arrays.equals(a, b) は
長さが同じか?
各要素が順番に全部同じか?
を見てくれるので、true になります。
多くの初心者がここでつまずきます。
「中身一緒なのに == が false って、どういうこと?」
→ == はオブジェクト(配列)の中身ではなく「同一の実体かどうか」を比べている。
「じゃあ中身を比べたいときは?」
→ Arrays.equals を使う。
この切り替えが、Arrays.equals を使う一番の理由です。
Arrays.equals が「同じ」とみなす条件
要素数・順番・値がすべて一致していること
Arrays.equals が true を返す条件はシンプルです。
配列の長さが同じであること
同じインデックスの要素同士が、すべて等しいこと
コードで見てみます。
int[] x = {1, 2, 3};
int[] y = {1, 2, 3};
int[] z = {1, 3, 2};
int[] w = {1, 2, 3, 4};
System.out.println(Arrays.equals(x, y)); // true(完全一致)
System.out.println(Arrays.equals(x, z)); // false(順番が違う)
System.out.println(Arrays.equals(x, w)); // false(長さが違う)
Javaつまり、「順番」も含めて同じである必要があります。
「ソート前後で、要素の集合が同じかどうか」みたいな話とは別です。
あくまで「配列として並んでいる形がまったく同じかどうか」です。
null に対しても安全に扱ってくれる
Arrays.equals は、片方または両方が null の場合にも対応しています。
int[] a = null;
int[] b = null;
int[] c = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true(両方 null)
System.out.println(Arrays.equals(a, c)); // false(片方だけ null)
Javaこのように、null チェック込みで安全に比較してくれるので、
自分で
if (a == null && b == null) ...
else if (a != null && b != null) ...
Javaなどと書くよりずっと楽です。
参照型配列(String[] など)のときに何を比較しているか
String[] なら equals で「文字列としての等しさ」を比べる
String[] や Integer[] のようなオブジェクトの配列を比較するとき、
Arrays.equals はどう動くでしょうか。
String[] s1 = {"apple", "banana"};
String[] s2 = {"apple", "banana"};
String[] s3 = {"apple", new String("banana")};
System.out.println(Arrays.equals(s1, s2)); // true
System.out.println(Arrays.equals(s1, s3)); // true
Javas3 の 1 番目の要素は new String("banana") なので、"banana" と同じ内容ですが別インスタンスです。
それでも Arrays.equals は true を返します。
なぜかというと、オブジェクト配列に対しては、
各要素の equals メソッド を使って比較しているからです。
String.equals は「文字列の中身が同じかどうか」を見ています。
だから、「別の String インスタンスでも、文字列として同じなら true」となるわけです。
自作クラスの配列でも equals の実装次第で比較できる
自分で定義したクラスの場合も同様です。
各要素の equals が「値としての等しさ」を判定するように実装されていれば、
Arrays.equals もそれを使います。
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return this.x == other.x && this.y == other.y;
}
}
Point[] p1 = { new Point(1, 2), new Point(3, 4) };
Point[] p2 = { new Point(1, 2), new Point(3, 4) };
System.out.println(Arrays.equals(p1, p2)); // equals を正しく実装していれば true
Java逆に、equals をちゃんと実装していないと、
「同じ値なのに false」となってしまいます。
つまり、
プリミティブ配列(int[] など) → 値同士を比較
参照型配列 → 各要素の equals を使って比較
という動きをします。
多次元配列には Arrays.deepEquals が必要になる
普通の equals だと「中まで見てくれない」問題
2次元配列(配列の配列)を考えてみます。
int[][] a = {
{1, 2},
{3, 4}
};
int[][] b = {
{1, 2},
{3, 4}
};
System.out.println(Arrays.equals(a, b)); // ?
System.out.println(Arrays.deepEquals(a, b)); // ?
JavaArrays.equals(a, b) は false になることが多いです。
なぜかというと、int[][] の「1次元目」は int[] の配列だからです。
Arrays.equals は、1次元目のそれぞれの要素(つまり int[])同士を、
「参照」として比較してしまいます。
a[0] と b[0] は中身が同じ int[] ですが、new された別インスタンスです。
なので a[0] == b[0] は false。結果として Arrays.equals(a, b) も false です。
「中にある配列の中身まで全部見てほしい」
→ そういうときに使うのが Arrays.deepEquals です。
System.out.println(Arrays.deepEquals(a, b)); // true
JavadeepEquals は、配列の中に配列があっても、
「再帰的に潜って」全要素を比較してくれます。
多次元配列で「本当に中身全部同じか?」を見たいときは、Arrays.deepEquals を使うと覚えておきましょう。
Arrays.equals をいつ使うか、設計としての感覚
「配列が同じものか?」ではなく「配列の中身が同じか?」を考える
配列の比較でまず考えるべきなのは、
「同じ配列インスタンスかどうか」を知りたいのか
「中身が同じ配列かどうか」を知りたいのか
です。
ほとんどの場面では、後者だと思います。
「ユーザーが選んだ選択肢の配列が、正解配列と同じか?」
「計算結果の配列が、期待結果と同じか?」
こういうときは、Arrays.equals(多次元なら deepEquals)の出番です。
逆に、「同じ配列オブジェクトを指しているか?」を知りたい場面はかなりレアで、
そのときだけ == を使います。
テストコードで特に威力を発揮する
配列を返すメソッドのテストを書くときにも、Arrays.equals は定番です。
int[] expected = {1, 2, 3};
int[] actual = someMethod();
if (Arrays.equals(expected, actual)) {
System.out.println("OK");
} else {
System.out.println("NG");
}
JavaJUnit などのテストフレームワークでも、内部的には Arrays.equals を使っています。
配列の結果を検証するたびに、
for 文を書いて一つ一つ compared するのは大変ですし、
バグが入りやすいので、「配列の比較=Arrays.equals」という癖をつけておくと楽になります。
まとめ:Arrays.equals を自分の中でどう位置づけるか
Arrays.equals を一言で言い直すと、
「配列同士の“中身の等しさ”を、長さと全要素の比較で判定してくれるメソッド」
です。
初心者の段階では、次のように覚えておくと扱いやすくなります。
- 配列同士を
==で比較しない。中身を比べたいなら Arrays.equals - プリミティブ配列は、要素の値を順番に全部比べてくれる
- オブジェクト配列は、各要素の equals を使って中身を比べてくれる
- 多次元配列は Arrays.deepEquals を使う(equals だと浅すぎる)
