1. Java でのオブジェクトの比較

Java では、オブジェクトは参照と値の両方で比較できます。

参考文献の比較

2 つの変数がメモリ内の同じオブジェクトを指している場合、これらの変数に格納されている参照は等しくなります。等価演算子 ( ==) を使用してこれらの変数を比較すると true が得られ、その結果は理にかなっています。ここではすべてがシンプルです。

コード コンソール出力
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

値による比較

ただし、2 つの変数が同一の 2 つの別個のオブジェクトを参照する状況がよく発生します。たとえば、同じテキストを含む 2 つの異なる文字列オブジェクトなどです。

異なるオブジェクトが同一であるかどうかを判断するには、equals()メソッドを使用します。例えば:

コード コンソール出力
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

メソッドはクラスequalsに限定されませんStringどのクラスにもそれがあります。

自分で作成したクラスでも、その理由は次のとおりです。



2.Objectクラス

Java のすべてのクラスはObjectクラスを継承します。Java の作成者はこのアプローチを思いつきました。

そして、クラスがそのObjectクラスを継承すると、そのクラスのすべてのメソッドが取得されますObject。そして、これは継承の重要な結果です。

言い換えれば、Objectコードで言及されていない場合でも、すべてのクラスにはそのクラスのメソッドがあります。

これらの継承されたメソッドには、オブジェクトの比較に関連するメソッドが含まれます。これらはequals()hashCode()メソッドです。

コード 実際には次のようになります。
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

上の例では、Personname パラメーターと age パラメーターを含む単純なクラスを作成しましたが、単一のメソッドは作成しませんでした。ただし、すべてのクラスがそのObjectクラスを継承するため、Personクラスには自動的に 2 つのメソッドが存在します。

方法 説明
boolean equals(Object obj)
現在のオブジェクトと渡されたオブジェクトを比較します
int hashCode()
現在のオブジェクトのハッシュコードを返します

equals絶対にすべてのオブジェクトがメソッドを持っており、異なるタイプのオブジェクトを相互に比較できることがわかりました。このようなコードはコンパイルされて完全に動作します。

コード コンソール出力
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3.equals()方法

equals()クラスから継承されたこのメソッドObjectは、現在のオブジェクトと渡されたオブジェクトを比較するための最も単純なアルゴリズムを実装します。オブジェクトへの参照を比較するだけです。

Personメソッドを呼び出さずに変数を比較するだけでも、同じ結果が得られますequals()。例:

コード コンソール出力
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

equalsメソッドが呼び出されるとa、変数に格納されている参照aと、変数に格納されている参照が単純に比較されますb

ただし、Stringクラスごとに比較の動作が異なります。なぜ?

なぜなら、クラスを作成した人がString独自のequals()メソッドの実装を作成したからです。

equals()メソッドの実装

次に、クラス内に独自の平等メソッドの実装を記述してみましょうPerson。4つの主なケースを検討します。

重要:
どのクラスがequalsメソッドをオーバーライドするかに関係なく、メソッドは常にObjectオブジェクトを引数として 受け取ります

シナリオ 1 : メソッドが呼び出される同じオブジェクトequalsもメソッドに渡されますequals。現在のオブジェクトと渡されたオブジェクトの参照が等しい場合、メソッドは を返す必要がありますtrue。オブジェクトはそれ自体に等しい。

コードでは次のようになります。

コード 説明
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


参照を比較する

シナリオ 2 :がメソッドnullに渡されますequals。比較するものがありません。メソッドが呼び出されるオブジェクトは明らかに null ではないため、この場合はequals返す必要があります。false

コードでは次のようになります。

コード 説明
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


参照の比較


渡されたオブジェクトはオブジェクトnullですか?

シナリオ 3 : ではないオブジェクトへの参照がメソッドにPerson渡されますequalsPersonオブジェクトは非オブジェクトと等しいですかPerson? Personそれはクラスの開発者が自由に決定できる問題です。

ただし、通常、オブジェクトが等しいとみなされるには、同じクラスに属している必要があります。したがって、クラスのオブジェクト以外のものがPersonequalsメソッドに渡された場合は、常に を返しますfalse。オブジェクトの種類を確認するにはどうすればよいでしょうか? そうです、instanceof演算子を使用することです。

新しいコードは次のようになります。

コード 説明
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


参照の比較


渡されたオブジェクトはオブジェクトnullですか?


渡されたオブジェクトがPerson

4. 2 つのPersonオブジェクトの比較

結局私たちはどうなったのでしょうか?メソッドの最後に到達すると、 でPersonはないオブジェクト参照が存在しますnull。したがって、それを に変換しPerson、両方のオブジェクトの関連する内部データを比較します。これが4 番目のシナリオです。

コード 説明
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


参照の比較


渡されたオブジェクトはオブジェクトnullですか? 渡されたオブジェクトが型キャスト


でない場合Person


では、2 つのオブジェクトをどのように比較するのでしょうかPerson? name名前 ( ) と年齢 ( age)が同じであれば、それらは等しいです。最終的なコードは次のようになります。

コード 説明
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


参照の比較


渡されたオブジェクトはオブジェクトnullですか? 渡されたオブジェクトが型キャスト


でない場合Person


しかし、それだけではありません。

まず、名前フィールドは であるStringため、メソッドを呼び出して名前フィールドを比較する必要がありますequals

this.name.equals(person.name)

次に、nameフィールドが次のような場合がありますnull。その場合、equalsそれを呼び出すことはできません。以下について追加のチェックが必要ですnull

this.name != null && this.name.equals(person.name)

ただし、名前フィールドがnull両方のオブジェクトにある場合でもPerson、名前は同じです。

4 番目のシナリオのコードは次のようになります。

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


年齢が等しくない場合は、
すぐに が等しいreturn false

場合、このメソッドを使用して比較する意味はありません。ここでは、2 番目のフィールドが に等しいか、そうでないかのどちらかです。 メソッドを使用して 2 つの名前フィールドを比較します。 this.namenullequalsnamenull

equals


5.hashCode()方法

両方のオブジェクトのすべてのフィールドの詳細な比較を実行することを目的としたメソッドに加えてequals、不正確ではあるが非常に迅速な比較に使用できる別のメソッドがありますhashCode()

数千の単語のリストをアルファベット順に並べ替えているときに、単語のペアを繰り返し比較する必要があると想像してください。そして単語は長く、たくさんの文字で構成されています。一般に、このような比較には非常に長い時間がかかります。

しかし、加速することは可能です。異なる文字で始まる単語があるとします。それらが異なることはすぐに明らかです。しかし、それらが同じ文字で始まる場合、結果がどうなるかはまだわかりません。単語が同じであることが判明する場合もあれば、異なることが判明する場合もあります。

このhashCode()方法も同様の原理を使用して機能します。オブジェクトに対してこれを呼び出すと、単語の最初の文字に似た数値が返されます。この数値には次の特性があります。

  • 同一のオブジェクトは常に同じハッシュコードを持ちます
  • 異なるオブジェクトが同じハッシュコードを持つことも、異なるオブジェクトのハッシュコードを持つこともできます
  • オブジェクトのハッシュコードが異なる場合、オブジェクトは明らかに異なります。

これをさらに明確にするために、これらのプロパティを言葉で再構成してみましょう。

  • 同じ単語は常に同じ最初の文字を持ちます。
  • 異なる単語の最初の文字が同じであることも、最初の文字が異なることもあります
  • 単語の最初の文字が異なる場合、その単語は明らかに異なります

最後のプロパティは、オブジェクトの比較を高速化するために使用されます。

まず、2 つのオブジェクトのハッシュコードが計算されます。これらのハッシュコードが異なる場合、オブジェクトは明らかに異なるため、それ以上比較する必要はありません。

ただし、ハッシュコードが同じ場合でも、equals メソッドを使用してオブジェクトを比較する必要があります。



6. コードでの契約

上記の動作は、Java のすべてのクラスで実装する必要があります。コンパイル中に、オブジェクトが正しく比較されているかどうかを確認する方法はありません。

Java プログラマは、equals() メソッドの独自の実装を記述して (クラス内の) 標準実装をオーバーライドする場合、前述の規則が満たされるような方法でメソッドObjectの独自の実装も記述しなければならないという共通の合意を持っています。hashCode()満足。

この取り決めは契約と呼ばれます。

equals()クラス内に のみ、または メソッドのみを実装した場合hashCode()、契約に重大な違反を犯したことになります (契約に違反したことになります)。こんなことはしないでください。

他のプログラマーがあなたのコードを使用すると、正しく動作しない可能性があります。さらに、上記の規約の遵守に依存するコードを使用することになります。

重要!

要素を検索するとき、すべての Java コレクションはまずオブジェクトのハッシュコードを比較し、その後でメソッドを使用して比較を実行しますequals

つまり、独自のクラスにequalsメソッドを与えても、独自のメソッドを記述していない場合hashCode()、またはメソッドの実装が間違っている場合、コレクションはオブジェクトで正しく動作しない可能性があります。

たとえば、リストにオブジェクトを追加し、メソッドを使用してそれを検索してcontains()も、コレクションでオブジェクトが見つからない可能性があります。