Help us understand the problem. What is going on with this article?

【Java】初心者が文字列比較で失敗する等価比較。間違いに気づいていない可能性も!?

More than 1 year has passed since last update.

二つの文字列が等価かどうか判定しようとした時などに、直感的には==を使いたくなってしまうと思います。ですが、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演算子を使った場合も明示的に利用する方法がありますが割愛させて頂きます)

StringConstantPoolを利用した場合
//1回目なので、通常通りメモリ領域が確保され生成される
String str = "hoge";
//すでに同じ値がString定数プールに存在するので、既存の参照先(アドレス値)と結びつける
String str_2 = "hoge";
StringConstantPoolを利用しない場合
//通常通りメモリ領域が確保され生成される
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では==で値の比較ができるようですね。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした