等値判定と等価判定
String a = "Hello";
String b = new String("Hello");
if(a == b) System.out.println("TRUE");
else System.out.println("FALSE");
早速ですが、上記のコードを実行すると何が出力されるでしょうか?
答えはFALSEです。
もやもやする方も納得のいく方も、次のコードを見てみてください。
String a = "Hello";
String b = "Hello";
if(a == b) System.out.println("TRUE");
else System.out.println("FALSE");
では、こちらのコードを実行すると何が出力されるでしょうか?
答えはTRUEです。
上記の2つの例を比べて、何か違和感を感じた方も多いのではないでしょうか。
sample1/sample2
ともに、変数aと変数bにHello
を代入しているだけですよね。
なぜ結果が異なるのでしょうか。
等値判定(==)
if文内で使用している演算子(==)は等値であるかどうかの判定、変数そのものの値を比較しているんですね。
なら全てFALSEにならないとおかしいじゃないか、という意見が出てくると思いますし、それはごもっともです。なぜならString型は生成されるたびにメモリ領域のどこかに必要な用量だけ確保されるからです。
つまり、String a = "Hello";
を実行した際に、メモリ上のaaaa番地を先頭としてHelloが格納され、変数a
にはアドレス番号であるaaaaが格納されるのです。
同様に、String b = "Hello";
を実行した際に、メモリ上のbbbb番地を先頭としてHelloが格納され、変数b
にはアドレス番号であるbbbbが格納されるのです。
定義するたびに新たに容量が確保され、それらの変数には個別のアドレス番地が格納されるということです。
そして、等値判定では変数そのもの、つまり各変数が保持しているアドレス番地が等しいかどうかを判定するので、当然FALSEでなくちゃおかしいのです。
ですが、文字列リテラルは、コンパイル時にプールと呼ばれる領域に保存されます。同じ内容の文字列リテラルは、プール内にある同じオブジェクトを参照するように最適化されます。
つまり、複数の変数がそれぞれ同じ文字列を持っていても容量の無駄使いとなる(文字列は書き換え不可で、個別に定義する必要がない)ので、コンパイル時には1つだけ生成してあとはみんな同じアドレスを参照しようね、ということになっているのです。
本来なら変数a
にaaaa
が、変数b
にbbbb
が代入されるはずだったのですが、コンパイル時にnnnn番地
にHelloが格納され、aもbもnnnnを保持することになるので、等値判定がTRUEとなるのです。
なんでnewで作ったらFALSEになるの
以上の説明から、同じ文字列を定義すると、1つにまとめてみんなそこを参照するようになるんでしたよね。ならなぜsample1
の結果はFALSEとなったのでしょうか?
これは単純で、newキーワードを使用すると、毎回新しいオブジェクトが生成されるのですが、オブジェクトは、ヒープと呼ばれる領域に生成されます。
つまり、""
で定義した場合とnew
で定義した場合では異なるメモリ領域を指すため、==演算子で比較するとfalseになるというだけの話でした。
もちろん、2つともnew
で定義しても、それらは別々のヒープ領域を使うので、文字列が同じでも==演算子の結果はFALSEとなります。
等価判定(equals)
では、サンプルコードの判定式を==
ではなくequals
で代用するとどうなるか。
String a = "Hello";
String b = "Hello";
String c = new String("Hello");
System.out.println(a.equals(b)); //true
System.out.println(a.equals(c)); //true
以上のような結果になります。
等価判定とは、対象の変数が指し示す内容が同じかどうかを判定しています。
つまり、変数そのものではなく、変数に入っているアドレスが指し示す先の内容が同じかどうかを見ているわけです。
だから、変数a
と変数c
にはそれぞれ個別のアドレス番地が格納されていますが、どちらの参照先もHello
という内容であるので、equalsの結果はtrueとなるわけですね。
まとめ
今回は等値判定(==)と等価判定(equals)の違いについてまとめてみました。
つまるところ、等値判定の方がより厳しい判定条件ということですね(雑)
ちなみに、equalsはプリミティブ型には使えないので、お気を付けください。