Stringオブジェクト(文字列)の比較についてです。
Javaを勉強し初めた入門段階で普通にif (str1 == str2)
と比較してしまい、何で値が同じなのにTrueを返さないんだと小一時間悩んだのはいい思い出です。
equalsメソッド
Stringの文字列を比較する場合はString.equals()
メソッドを使用します。そこで、同じ文字列を比較するのに関係演算子(==)を使用した場合と、equalsメソッドを使用した場合で比較してみたいと思います。
class Sample1 { public static void main(String[] args) { String str1 = new String("hoge"); String str2 = new String("hoge"); System.out.println(str1 == str2); System.out.println(str1.equals(str2)); } }
結果はこうなります。
$ javac Sample1.java $ java Sample1 false true
なぜこうなるかと言うと、Stringオブジェクトは参照型データであるため、str1とstr2の変数にはそれぞれ直接文字列が入っている訳ではなく、その文字列の参照先が入っているからです。参照先を住所なんかで考えると分かり易いでしょう。
str1とstr2にそれぞれ"hoge"をnewで生成することで別の参照先がセットされます。そうすると関係演算子(==)の場合は文字列同士を比較したのではなく参照先を比較した事になり、false
を返します。
逆にequalsメソッドの場合は何をしているかと言うと、参照先の中身(文字列)同士を比較してくれるのでtrue
を返すという訳です。
よくある注意事項
1.NullPointerException
例えば以下のようなソースがあります。
public boolean hoge(String str1, String str2) { return str1.equals(str2); }
これは文法的にはまったく問題の無いコードでコンパイルも通りますが、str1にNullが入ってくるとNullPointerExceptionが発生しますので気をつけましょう(equalsメソッドを呼びだそうとしてもstr1がStringオブジェクトではなくNullであるため。)
ですのでNullかどうか判定する必要が出てくるのですが、その場合はequalsメソッドではなく、関係演算子(==)を使います。
public boolean hoge(String str1, String str2) { if (str1 == null) { return false; } return str1.equals(str2); }
ちなみにstr2にNullが入ってきた場合は例外は発生せずにfalse
が返ります。
2.別の参照型同士での比較
StringとIntegerを比較してしまっていた場合などです。
public boolean hoge(String userId) { Integer userIdNew = 〜 return userId.equals(userIdNew); }
同じデータだけど保持するClassでデータ型が違ったりすることもありますが、この場合でもコンパイルが通ってしまうので意外に気付かない時もあります。しかし結果がtrue
になることはありませんので注意が必要です。
ついでなのでString.equals()
メソッドのソースを見てみます。※Java8
public boolean equals(Object anObject) { if (this == anObject) { (1) return true; } if (anObject instanceof String) { (2) String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
今回の記事との関連ポイントは(1)と(2)の箇所です。
(1)は関係演算子(==)で比較しているので参照先が同じデータかを比較しています。参照先が同じなら当然中身も同じなのでtrue
が返ります。
(2)は引数のオブジェクトがStringかどうかを聞いています。なので、上記でも書いてあるように引数がNull、又は、Integer等の別の参照型オブジェクトであった場合は全てfalse
が返るという訳です。
3.他の参照型の比較方法
一番使用機会が多いという事でStringメインに書いてきましたが、Stringに限った話しではありません。他の参照型(Integer、Boolean、Short、Date等)も同様に比較する時はequals
メソッドを使うようにしましょう。
class Sample2 { public static void main(String[] args) { Integer int1 = new Integer(100); Integer int2 = new Integer(100); System.out.println(int1 == int2); System.out.println(int1.equals(int2)); Boolean boo1 = new Boolean(true); Boolean boo2 = new Boolean(true); System.out.println(boo1 == boo2); System.out.println(boo1.equals(boo2)); } }
実行してみるとStringと同じような結果になるのがわかります。
$ javac Sample2.java $ java Sample2 false true false true