- ベストアンサー
Javaの文字列比較
初歩的な質問ですが、Javaで文字列比較は moji == hikaku でなくて moji.equals(hikaku)でないとダメなのでしょうか? 仕様やオブジェクト指向だからでなく、具体的な理由が知りたいです。 数年後には理解できるかもしれませんので難しくても構いません。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
equals() が内容の比較をすると決まっているわけではなく、各クラスで「うまいこと」再定義されているという方が正確だと思います。 放っておいたら、Object#equals() がそのまま使われて、これは参照値の比較ですよね。 要は演算子 == が Java で再定義できず、一般的な規約としては「参照値の比較です」と == を定義するしかしょうがなかったんだと思います。 == をプログラマが再定義できれば、わざわざ equals() などという演算子を別建てで用意する意味はないと思います。
その他の回答 (6)
- aton
- ベストアンサー率47% (160/334)
No.5です。若干訂正します。 訂正前:"=="は名前(参照同一性)の比較,"equals"は内容の比較 訂正後:"=="は名前(参照同一性)の比較,"equals"は(普通は)それ以外(例:内容)の比較 #もちろん,"equals"が特に定義されていない場合は,参照同一性の比較であってもかまわない。 要は,"=="は常に参照同一性の比較に使われ,それ以外に独自の比較方法を定義する場合は"equals"を用いる,という定義で言語としての統一性を保っているということです。 "=="が再定義可能にすると,この統一性は失われます。また場合によっては参照同一性の比較ができなくなります。 なお,ここから先は余談ですが,この問題と,Javaにおいて,高速化のために同一内容のStringは同じインスタンスとして扱うよう内部で実装されていることは,独立と考えます。この話は本質的にJava内部の実装の問題であり,プログラマーから見える界面仕様ではありません。(もちろん,知っておくと処理の高速化を図れたりもしますが。)
- aton
- ベストアンサー率47% (160/334)
理由はNo.1~3の方が書かれているとおりですので,私はその一つ先,No.4の方が示された(そして恐らく質問者の方も抱かれるであろう)問題,すなわち,「なぜその2つの方法のうち,Javaの言語設計者は"equals"の方を選んだのか」について私なりの答えを示したいと思います。 要するにこれは,「見た目*だけ*のわかりやすさ」を優先するか,「言語としての統一性を保つことで,言語としてのわかりやすさ」を重視するかというスタンスの違いだと思います。 No.1~3の方が書かれているとおり,簡単に言うと,"=="は名前(識別子)の比較,"equals"は中身の比較です。例えばこれをファイルに喩えてみると,ファイル名(フルパス)が等しいかどうかが前者です。これが同じであれば,同じものを指していることは明らかですから,自動的に中身も等しいと考えられます。一方,ファイル名(フルパス)が異なっていても,ファイルをコピーすれば,同じ内容のファイルを作ることは当然できます。このとき,ファイルの中身を比較するのが,"equals"です。 比較には2種類あり,両方を使い分ける必要があることはこれでわかったかと思います。問題は,ここでStringだけを特別扱いして,"=="で中身の比較を行うという仕様にするかどうか,ということです。 そのようにStringだけを特別扱いすると,見た目上は直観的な記述になります。その代わり,言語としての統一性("=="は名前の比較,"equals"は内容の比較というルール)が崩れます。このルールが崩れると,当然「他にも特別扱いされるクラスがあってもいいんじゃないか」という疑念につながり,「どのクラスでは"=="で内容比較をして,どのクラスではしないのか」ということをいちいち覚える必要が出てきます。こうした混乱を避けるためには,多少見た目がぎこちなくても,統一性を重視する,という立場はあってしかるべきだと考えます。 ちなみに,比較演算子に2種類の表記があり,それぞれに意味が異なる言語は他にもあります。例えばPerlでは,(名前/内容の比較という区別ではありませんが)数値コンテキスト用と文字列コンテキスト用の比較演算子で,振る舞いが違います。 LL言語系の人は,見た目の簡単さ(しばしば syntax sugar と呼ばれるもの)を重視します。たしかにそれは重要なことだと思いますが,言語自体の設計の整合性に混乱を与えるような syntax sugar は,害のほうが大きいというのが私の考えです。
- koko_u_
- ベストアンサー率18% (459/2509)
いやいや、まったく尤もな疑問ですね。 Java の実装担当者が寝惚けていたとしか思えませんね。 equals() を使わざるを得ない理由は下で縷々述べられていますが、 どう考えても「文字列として等しい」を普通に == で記述する方が妥当だし、 コードも見易くなることは明らかです。
Javaでは、基本型(intなど)は変数にその値が保管されますが、オブジェクトは「参照(リファレンス)」が保管されます。==は、その変数の値を直接比較します。intなどはそのまま比較されますが、オブジェクトの場合には変数には参照が入っていますから、参照が等しいかどうかを比較するわけです。参照というのは、そのオブジェクトのある場所を示す値と考えてください。 例えば、同じ「A」という値を保持するStringオブジェクトであっても、異なるオブジェクトであれば、異なる場所に保管されますから参照は異なる値になります。従って、trueにはならないのです。 これに対し、equalsは、参照先のオブジェクト自体を比較します(正確にはそのオブジェクトに実装されているequalsメソッドを呼び出して値をチェックします)。だから、オブジェクトどうしの比較はequalsを使うのです。 ただし、リテラル(直接テキストを"Hello"のように書いた値)は、==で比較できます。これは、Javaではリテラルの値は「定数プール」と呼ばれる独自の場所で管理されるためです。例えば、"Hello"というリテラルは、どこで何箇所使われてもすべて定数プール内の同じ"Hello"リテラルを参照します。参照する値がすべて同じなので、==でも等しいと確認できるわけです。
- auty
- ベストアンサー率58% (284/486)
>>> Javaで文字列比較は moji == hikaku でなくて moji.equals(hikaku)でないとダメなのでしょうか? ダメです。!! ’比較’が何と何を比較するかを考えて見ましょう。 ・ moji == hikaku の場合 参照を比較する。(ちょうど整数を比較するように) C言語をご存知ならば、ポインタ(アドレス)を比較する。 結局オブジェクトを比較していることになります。 ・ moji.equals(hikaku) の場合 文字列そのものを比較する。 この場合、文字列は、Stringオブジェクトmoji、hikakuの一部分だと考えてください。 もし、moji == hikakuが成立すれば、全く同じオブジェクトなので当然その一部である文字列も等しくなります。 逆は成り立ちません。 一部が等しくても、すなわち moji.equals(hikaku)が成り立ってもその所有者(オブジェクト)が等しいとは限りません。 簡単な例を挙げてみましょう。 ------------------------------------------------------------ 太郎という名の人(オブジェクト)はたくさんいます。 太郎という名は人(オブジェクト)の一部分です。。 山田太郎は一人しかいないとします。 日本太郎も一人とします、 山田太郎==山田太郎 の場合同一本人ですから当然その文字列「太郎」は同じです。 山田太郎.equals(日本太郎)は、実はその名前(文字列「太郎」)同士を比較して、 true を返します。つまり異なった人でも同じ名前の人がいるわけです。 ------------------------------------------------------------ なお、Stringオブジェクトがそのような文字列の比較をする(equals())のは、オーバーライドして設定しているからです。
- guppy_i
- ベストアンサー率50% (1/2)
「==」は、メモリ上のアドレスが等しいかどうかを判定します。 「moji」と「hikaku」に格納した文字列そのものが同じでも、 別々のメモリを確保して文字列を置いているため、「==」では よろしくないということになります。 一方、Stringのメソッド「equals」は、文字列そのものが同じで あるかどうかを判定します。 ちなみに、「moji == hikaku」を実行すると、trueが返ります。 これは、javaが気を利かせてそうなっているだけなので、 (それならいいじゃんと言われそうですが(^^;) オブジェクトの 比較は「equals」メソッドを使ってする癖をつけた方が良いです。 他のクラスをバリバリ使っていくようになると、違いが明確に 理解できるようになると思います。 他のクラス(Vector)での例 import java.util.Vector; public class Test { public static void main(String[] args) { Vector a = new Vector(); Vector b = new Vector(); Object o = new Object(); a.add(o); b.add(o); System.out.println(a == b); // falseと表示されます。 System.out.println(a.equals(b)); // trueと表示されます。 } }