二つの文字列が等価かどうか判定しようとした時などに、直感的には==
を使いたくなってしまうと思います。ですが、Javaでは文字列の比較の際に==
ではなくequalsメソッド
を使わないといけません。この理由を理解するためには、Javaでは文字列はオブジェクト型(参照型)であるということと、Stringの特徴を理解していく必要があると思います。
##オブジェクト型(参照型)の変数
オブジェクト型の場合、通常はオブジェクトの設計図であるクラスからnew演算子
を使用して必要なメモリ領域を確保してインスタンス生成を行います。そしてJavaの場合、オブジェクト型は参照渡し(ポインタ渡し) となります。つまり変数には値ではなく実際の値が格納されているメモリのアドレス値を格納しているということになります。ちなみに、intやbooleanなどはプリミティブ型といって、こちらの変数は値自体を保持しています。
Stringオブジェクトの生成
String str = "hoge";
通常はオブジェクト型の場合、new演算子
を使ってオブジェクトの生成を行うのですが、Stringの場合はnew演算子
を使わずに生成できる便利な仕組みが用意されているので= ""
だけで生成することができます。また、= ""
で生成することで後述するString Constant Poolという仕組みの恩恵を受けることができます。
##String Constant Pool(文字列の再利用のための仕組み)
String Constant Poolとは、新しくStringオブジェクトを生成する際にメモリの節約をするための仕組みです。String定数プールという特別なメモリ領域に値が保持され、新たに文字列の生成が行われる際に、同じ内容のStringオブジェクトがないかプールをチェックして、あった場合は参照を再利用してくれます。この仕組みを利用するにはnew演算子
を使わずに= ""
でStringオブジェクトの生成をします。(new演算子
を使った場合も明示的に利用する方法がありますが割愛させて頂きます)
//1回目なので、通常通りメモリ領域が確保され生成される
String str = "hoge";
//すでに同じ値がString定数プールに存在するので、既存の参照先(アドレス値)と結びつける
String str_2 = "hoge";
//通常通りメモリ領域が確保され生成される
String str = new String("hoge");
//String Constant Poolが利用されないので、通常通りメモリ領域が確保され生成される
String str_2 = new String("hoge");
ちなみに、Stringが不変である理由はこの仕組みにあります。複数の同じ文字列が同じ参照をしている時に、どれかを変更してしまうと他の全てにも影響が出てしまうので変更不可となっているようです。
==でイケると勘違いしてしまうことがある
String str = "hoge";
String str_2 = "hoge";
//このif文の条件はtrueですが、それは値が同じだからではなく参照先(アドレス値)が同じだからです。
if(str == str_2){
System.out.println("値は同じです");
}else{
System.out.println("値は違います");
}
上記コードのif文の条件がtrueとなるので、値の比較ができているように錯覚してしまうかもしれませんが、これは参照先(アドレス値)の比較をして、String Constant Poolの再利用によって参照先(アドレス値)が同じだったためtrueとなるのです。なので、下記のような場合には成立しません。
String str = "hoge";
String str_2 = new String("hoge");
//この場合は参照の再利用はされず、strとstr_2の参照先(アドレス値)は異なるため条件はfalseになります。
if(str == str_2){
System.out.println("値は同じです");
}else{
System.out.println("値は違います");
}
きちんと値の比較をする方法
値自体を比較するためには、冒頭で書いた通りequalsメソッド
を使うようにしましょう。
String str = "hoge";
String str_2 = new String("hoge");
//equalsメソッドを使うことで、値自体の比較ができます。
if(str.equals(str_2)){
System.out.println("値は同じです");
}else{
System.out.println("値は違います");
}
まとめ
Javaの場合の文字列比較をきちんとするための方法を紹介させて頂きました。ポイントは、String型はオブジェクト型であるということと、オブジェクト型は値ではなく参照先(アドレス値)を保持しているということだと思います。この二つさえ理解できればequalsメソッド
を使う理由が理解できると思います。ちなみに、比較的新しい言語のSwiftやKotlinでは==
で値の比較ができるようですね。