Java | Java 標準ライブラリ:Arrays.equals

Java Java
スポンサーリンク

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 になります。
ab は別々に 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
Java

s3 の 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));  // ?
Java

Arrays.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
Java

deepEquals は、配列の中に配列があっても、
「再帰的に潜って」全要素を比較してくれます。

多次元配列で「本当に中身全部同じか?」を見たいときは、
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");
}
Java

JUnit などのテストフレームワークでも、内部的には Arrays.equals を使っています。

配列の結果を検証するたびに、
for 文を書いて一つ一つ compared するのは大変ですし、
バグが入りやすいので、「配列の比較=Arrays.equals」という癖をつけておくと楽になります。


まとめ:Arrays.equals を自分の中でどう位置づけるか

Arrays.equals を一言で言い直すと、

「配列同士の“中身の等しさ”を、長さと全要素の比較で判定してくれるメソッド」

です。

初心者の段階では、次のように覚えておくと扱いやすくなります。

  • 配列同士を == で比較しない。中身を比べたいなら Arrays.equals
  • プリミティブ配列は、要素の値を順番に全部比べてくれる
  • オブジェクト配列は、各要素の equals を使って中身を比べてくれる
  • 多次元配列は Arrays.deepEquals を使う(equals だと浅すぎる)

タイトルとURLをコピーしました