2次元配列の比較に要注意!! containsメソッドの落とし穴

こんにちは。
二年目社員の佐藤です。

日常ではSEとしてテストに明け暮れる中、細かいバグの改修も行うようになってきました。
まだまだペーペーの私ですが、最近では分からないことを簡単に上長や先輩に聞けなくなってきました。

エンジニアを名乗る以上、聞いてばかりではダメですよね。
問題解決の幅を広げるため、自分で答えを見つけ出す必要がある場面も多々有ります。
平成生まれの第一世代としては慣れない事ですが、自分で決めた仕事です。
頑張るしかないっしょ!!

さて、そんなわけで今回は現場で遭遇したバグの一つをご紹介。
誰かの役に立つといいな。

バグ

// 配列aの生成
String[] a = new String[3];

a[0] = "AAA";
a[1] = "BBB";
a[2] = "CCC";

// Listの生成
List<String[]> list = new ArrayList<String[]>();
// Listに配列aを格納
list.add(a);

// 配列bの生成
String[] b = new String[3];
b[0] = "AAA";
b[1] = "BBB";
b[2] = "CCC";

// containsを利用したaとbの比較
if (list.contains(b)) {
    System.out.println("TRUE");
} else {
    System.out.println("FALSE");
}

実行結果

FALSE

Why java language!?!?
正しいと思ってたことが間違ってる。
焦りますよね。ミニパニックです。

私の感想は置いといて解答を見てみましょう。

解答

// 配列aの生成
String[] a = new String[3];
a[0] = "AAA";
a[1] = "BBB";
a[2] = "CCC";

// Listの生成
List <String[]> list = new ArrayList<String[]>();
// Listに配列aを格納
list.add(a);


// 配列bの生成
String[] b = new String[3];
b[0] = "AAA";
b[1] = "BBB";
b[2] = "CCC";

// Arrays.equalsを利用した配列の比較
for (String[] hairetu : list) {
    if (Arrays.equals(hairetu,  b)) {
        System.out.println("TRUE");
        brake;
    }
}

実行結果

TRUE

どうにか欲しい解答を得ることが出来ました。

解説

配列とListの違いを明確に理解していなかったようです。
どちらも複数のデータを格納するためのデータ構造でありながら、その違いは生成時に大きさを宣言する必要の有無がある。

この他にも実は違いがあったのです。
なんと配列はObject.equals()メソッドをオーバライドしないそうです。つまりequals()の実装は入れる中身ではなく参照を比較してしまう。
そして今回のケースで使われているcontainsメソッドのAPIを確認すると、、

contains
boolean contains(Object b)
リストに指定の要素がある場合にtrueを返します。つまり、リストに、(o==null ? e==null : o.equals(e)) となる要素 e が 1 つ以上含まれている場合にだけ true を返します。

Java™ Platform, Standard Edition 7 API 仕様

つまりList内の配列の中身でなく参照を比較していたのようです。

まとめ

今回のケースから学んだことの一つに配列の比較にequals()メソッドを使う際は、その中身でなく参照を比較してしまう点を理解すること。
また同様にデータ構造が多次元に渡る場合は、どの部分を比較したいのか明確に理解し的確に表すこと。

以上、二点の重要性を確認することが出来ました。

ささやかな事案でしたが誰かの役にたてば幸いです。
佐藤がお送りしました。

記事をシェア
MOST VIEWED ARTICLES