こんにちは!今日は非常に重要で興味深いトピック、つまりJava でのオブジェクト間の平等()比較について話します。実際、Java ではどのような場合にオブジェクトAがオブジェクトBと等しくなるのでしょうか? 例を書いてみましょう:
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car car1 = new Car();
car1.model = "Ferrari";
car1.maxSpeed = 300;
Car car2 = new Car();
car2.model = "Ferrari";
car2.maxSpeed = 300;
System.out.println(car1 == car2);
}
}
コンソール出力:
false
わかった、やめて。実際、なぜこれら 2 台の車は同等ではないのでしょうか? それらに同じプロパティを与えましたが、比較の結果は false です。答えは簡単です。演算子は==
オブジェクトのプロパティではなくリンクを比較します。2 つのオブジェクトに 500 個の同一のプロパティがある場合でも、比較の結果は false になります。結局のところ、リンクは 2 つの異なるオブジェクトcar1
、つまり 2 つの異なるアドレスを指しています。人々を比較する状況を想像してみてください。あなたと同じ名前、目の色、年齢、身長、髪の色などを持つ人は、おそらく世界に存在します。つまり、あなたは多くの点で似ていますが、それでも双子ではなく、特に同一人物ではありません。 演算子は、2 つのオブジェクトを比較するために使用する場合、ほぼ同じロジックを適用します。しかし、プログラムに別のロジックが必要な場合はどうすればよいでしょうか? たとえば、プログラムが DNA 分析をシミュレートする場合です。彼女は 2 人の DNA コードを比較し、双子であることを判断しなければなりません。 car2
==
public class Man {
int dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1 == man2);
}
}
コンソール出力:
false
結果が同じになるのは当然です (結局のところ、何も変更しませんでした)。しかし、今では満足できません。実際、実生活では、DNA 分析によって、双子であることが 100 パーセント保証されます。しかし、私たちのプログラムとオペレーターは==
そうではないことを教えてくれます。この動作を変更して、DNA 検査が一致する場合にプログラムが正しい結果を生成するようにするにはどうすればよいでしょうか? この目的のために、Java では特別なメソッド、equals()が作成されました。
Java の Equals() メソッド
前に説明したメソッドと同様にtoString()
、equals() はObject
Java で最も重要なクラスであるクラスに属しており、他のすべてのクラスはそこから派生します。ただし、equals() 自体はプログラムの動作をまったく変更しません。
public class Man {
String dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = "111122223333";
Man man2 = new Man();
man2.dnaCode = "111122223333";
System.out.println(man1.equals(man2));
}
}
コンソール出力:
false
まったく同じ結果ですが、ではなぜこの方法が必要なのでしょうか? :/ それは簡単です。実際、このメソッドはクラス自体に実装されているので使用していますObject
。そして、クラス コードを調べてObject
、このメソッドがその中でどのように実装され、何を行うのかを確認すると、次のことがわかります。
public boolean equals(Object obj) {
return (this == obj);
}
これが、プログラムの動作が変わらない理由です。クラスのequals()メソッド内には、Object
同じ参照比較があります==
。しかし、このメソッドの重要な点は、それをオーバーライドできることです。オーバーライドとは、クラス内に独自のquals() メソッドを記述しMan
、それを希望どおりに動作させることを意味します。ここで、チェックがman1.equals(man2)
本質的に と同じことを行うことに満足していませんman1 == man2
。この状況では次のようにします。
public class Man {
int dnaCode;
public boolean equals(Man man) {
return this.dnaCode == man.dnaCode;
}
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1.equals(man2));
}
}
コンソール出力:
true
全く違う結果になりました!標準のメソッドの代わりに独自のequals()メソッドを作成することで、正しい動作を実現しました。今度は、2人の人物が同じDNAコードを持っている場合、プログラムは「DNA分析により、彼らは双子であることが判明しました」と通知し、trueを返します。クラスでequals()メソッドをオーバーライドすることで、必要なオブジェクト比較ロジックを簡単に作成できます。オブジェクトの比較については、一般的な観点からのみ触れてきました。このトピックに関する別の大規模な講義が今後も予定されています(興味があればすぐに読むことができます)。
Java での文字列比較 - 文字列比較
文字列の比較を他のものとは別に扱うのはなぜでしょうか? まあ、実際のところ、プログラミングにおけるセリフはまったく別の話です。まず、人類が作成したすべての Java プログラムを取り上げると、その中のオブジェクトの約 25% が Java プログラムで構成されています。したがって、このトピックは非常に重要です。第 2 に、文字列を比較するプロセスは他のオブジェクトとはまったく異なります。簡単な例を見てみましょう。public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2);
}
}
コンソール出力:
false
しかし、なぜ嘘なのでしょうか?行は一言一句まったく同じです :/これは、演算子が ==
参照を比較しているためであると推測できます。結局のところ、s1
それらはs2
メモリ内に異なるアドレスを持っています。この考えが浮かんだ場合は、例をやり直してみましょう。
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
System.out.println(s1 == s2);
}
}
これで 2 つのリンクもありますが、結果は逆に変わりました。コンソール出力:
true
完全に混乱していますか?:) それを理解しましょう。オペレータは==
実際にメモリ内のアドレスを比較します。このルールは常に有効であり、疑う必要はありません。これは、s1 == s2
true を返す場合、これら 2 つの文字列がメモリ内に同じアドレスを持つことを意味します。そして本当にその通りです!String pool
文字列を保存するための特別なメモリ領域である文字列プール ( ) について知りましょう。文字列プールは、プログラムで作成したすべての文字列値を保存するための領域です。何のために作られたのでしょうか?前述したように、文字列はすべてのオブジェクトの大部分を占めます。大規模なプログラムでは、多数の行が作成されます。メモリを節約するには、これが必要ですString Pool
。必要なテキストを含む行がそこに配置されます。将来、新しく作成されたリンクは同じメモリ領域を参照するため、毎回追加のメモリを割り当てる必要はありません。を記述するたびにString = “........”
、プログラムは文字列プールにそのようなテキストを含む行があるかどうかを確認します。存在する場合、新しいものは作成されません。そして、新しいリンクは、この文字列が保存されている文字列プール内の同じアドレスを指します。したがって、プログラムに書くと、
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
リンクはs2
とまったく同じ場所を指しますs1
。最初のコマンドは、必要なテキストを含む新しい行を文字列プールに作成しました。2 番目のコマンドでは、単に と同じメモリ領域を参照しましたs1
。同じテキストで少なくとも 500 行以上作成できますが、結果は変わりません。停止。しかし、なぜこの例が以前はうまくいかなかったのでしょうか?
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2);
}
}
直感的には、その理由はすでに推測できていると思います :) さらに読み進める前に、推測してみてください。これら 2 つの行は異なる方法で作成されたことがわかります。1 つはオペレーターの助けを借りた場合new
、もう 1 つはオペレーターの助けを借りない場合です。まさにこれが理由です。new オペレーターは、オブジェクトの作成時に、そのオブジェクトに対してメモリー内の新しい領域を強制的に割り当てます。そして、 で作成された行はnew
、最終的に にはなりませんString Pool
。テキストが 'a の同じ行とまったく同じであっても、別のオブジェクトになりますString Pool
。つまり、次のコードを書くとします。
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
String s3 = new String("JavaRush is the best site to learn Java!");
}
}
メモリ内では次のようになります。 新しいオブジェクトが作成されるたびに、new
たとえ新しい行内のテキストが同じであっても、メモリ内に新しい領域が割り当てられます。演算子については整理できたようです==
が、新しい友人であるequals()メソッドについてはどうでしょうか?
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1.equals(s2));
}
}
コンソール出力:
true
面白い。私たちは正確に何を知っておりs1
、s2
メモリ内のさまざまな領域を指しています。しかし、それにもかかわらず、equals() メソッドはそれらが等しいと示します。なぜ?上で、equals() メソッドをクラス内でオーバーライドして、必要な方法でオブジェクトを比較できると述べたことを覚えていますか? それが彼らがクラスでやったことですString
。オーバーライドされたequals()メソッドがあります。また、リンクではなく、文字列内の文字のシーケンスを比較します。また、文字列内のテキストが同じであれば、それらがどのように作成され、どこに保存されるか (文字列プールか別のメモリ領域か) は関係ありません。比較の結果は true になります。ちなみに、Java では、大文字と小文字を区別せずに文字列を正しく比較できます。通常の状況では、たとえば大文字で行の 1 つを記述すると、比較の結果は false になります。
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JAVARUSH - ЛУЧШИЙ САЙТ ДЛЯ ИЗУЧЕНИЯ JAVA!");
System.out.println(s1.equals(s2));
}
}
コンソール出力:
false
この場合、クラスにはString
メソッドがありますequalsIgnoreCase()
。比較の主な点が特定の文字の大文字小文字ではなく、一連の文字である場合は、これを使用できます。たとえば、これは 2 つの電子メール アドレスを比較する場合に役立ちます。
public class Main {
public static void main(String[] args) {
String address1 = "Moscow, Academician Korolev street, 12";
String address2 = new String("Г. МОСКВА, УЛ. АКАДЕМИКА КОРОЛЕВА, ДОМ 12");
System.out.println(address1.equalsIgnoreCase(address2));
}
}
この場合、同じアドレスについて話していることは明らかなので、このメソッドを使用するのはequalsIgnoreCase()
正しい決定です。
String.intern() メソッド
このクラスにはString
別のトリッキーなメソッドがありますintern()
。このメソッドは'om.intern()
で直接動作しますString Pool
。intern()
文字列に対して メソッドを呼び出すと、次のようになります。
- 文字列プールにこのテキストを含む文字列があるかどうかを確認します
- 存在する場合は、プール内のそのリンクを返します。
- そうでない場合は、このテキストを含む行を文字列プールに配置し、その行へのリンクを返します。
intern()
の文字列参照と比較できます。 String Pool
==
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2.intern());
}
}
コンソール出力:
true
以前は、 を使用せずにそれらを比較するとintern()
、結果は false でした。ここでメソッドは、「JavaRush - Java を学習するのに最適なサイト!」というintern()
テキストを含む行があるかどうかをチェックしました。文字列プール内。もちろんそれはそこにあります。私たちが書いたときにそれを作成しました
String s1 = "JavaRush is the best site to learn Java!";
参照s1
とメソッドによって返された参照がs2.intern()
メモリ内の同じ領域を指していることが確認されました。もちろん、それらは同じ領域を指しています:) 要約すると、主なルールを覚えて使用してください: 文字列を比較するには、常にequals()を使用してください。方法!文字列を比較するときは、ほとんどの場合、参照やメモリ領域などではなく、文字列を比較することを意味します。equals() メソッドは、必要なことを正確に実行します。ご自身で勉強できるリンクをいくつか紹介します。
GO TO FULL VERSION