1. Java でのオブジェクトの比較
Java では、オブジェクトは参照と値の両方で比較できます。
参考文献の比較
2 つの変数がメモリ内の同じオブジェクトを指している場合、これらの変数に格納されている参照は等しくなります。等価演算子 ( ==
) を使用してこれらの変数を比較すると true が得られ、その結果は理にかなっています。ここではすべてがシンプルです。
コード | コンソール出力 |
---|---|
|
|
値による比較
ただし、2 つの変数が同一の 2 つの別個のオブジェクトを参照する状況がよく発生します。たとえば、同じテキストを含む 2 つの異なる文字列オブジェクトなどです。
異なるオブジェクトが同一であるかどうかを判断するには、equals()
メソッドを使用します。例えば:
コード | コンソール出力 |
---|---|
|
|
メソッドはクラスequals
に限定されませんString
。どのクラスにもそれがあります。
自分で作成したクラスでも、その理由は次のとおりです。
2.Object
クラス
Java のすべてのクラスはObject
クラスを継承します。Java の作成者はこのアプローチを思いつきました。
そして、クラスがそのObject
クラスを継承すると、そのクラスのすべてのメソッドが取得されますObject
。そして、これは継承の重要な結果です。
言い換えれば、Object
コードで言及されていない場合でも、すべてのクラスにはそのクラスのメソッドがあります。
これらの継承されたメソッドには、オブジェクトの比較に関連するメソッドが含まれます。これらはequals()
とhashCode()
メソッドです。
コード | 実際には次のようになります。 |
---|---|
|
|
上の例では、Person
name パラメーターと age パラメーターを含む単純なクラスを作成しましたが、単一のメソッドは作成しませんでした。ただし、すべてのクラスがそのObject
クラスを継承するため、Person
クラスには自動的に 2 つのメソッドが存在します。
方法 | 説明 |
---|---|
|
現在のオブジェクトと渡されたオブジェクトを比較します |
|
現在のオブジェクトのハッシュコードを返します |
equals
絶対にすべてのオブジェクトがメソッドを持っており、異なるタイプのオブジェクトを相互に比較できることがわかりました。このようなコードはコンパイルされて完全に動作します。
コード | コンソール出力 |
---|---|
|
|
|
|
3.equals()
方法
equals()
クラスから継承されたこのメソッドObject
は、現在のオブジェクトと渡されたオブジェクトを比較するための最も単純なアルゴリズムを実装します。オブジェクトへの参照を比較するだけです。
Person
メソッドを呼び出さずに変数を比較するだけでも、同じ結果が得られますequals()
。例:
コード | コンソール出力 |
---|---|
|
|
equals
メソッドが呼び出されるとa
、変数に格納されている参照a
と、変数に格納されている参照が単純に比較されますb
。
ただし、String
クラスごとに比較の動作が異なります。なぜ?
なぜなら、クラスを作成した人がString
独自のequals()
メソッドの実装を作成したからです。
equals()
メソッドの実装
次に、クラス内に独自の平等メソッドの実装を記述してみましょうPerson
。4つの主なケースを検討します。
equals
メソッドをオーバーライドするかに関係なく、メソッドは常にObject
オブジェクトを引数として 受け取ります
シナリオ 1 : メソッドが呼び出される同じオブジェクトequals
もメソッドに渡されますequals
。現在のオブジェクトと渡されたオブジェクトの参照が等しい場合、メソッドは を返す必要がありますtrue
。オブジェクトはそれ自体に等しい。
コードでは次のようになります。
コード | 説明 |
---|---|
|
参照を比較する |
シナリオ 2 :がメソッドnull
に渡されますequals
。比較するものがありません。メソッドが呼び出されるオブジェクトは明らかに null ではないため、この場合はequals
返す必要があります。false
コードでは次のようになります。
コード | 説明 |
---|---|
|
参照の比較 渡されたオブジェクトはオブジェクト null ですか? |
シナリオ 3 : ではないオブジェクトへの参照がメソッドにPerson
渡されますequals
。Person
オブジェクトは非オブジェクトと等しいですかPerson
? Person
それはクラスの開発者が自由に決定できる問題です。
ただし、通常、オブジェクトが等しいとみなされるには、同じクラスに属している必要があります。したがって、クラスのオブジェクト以外のものがPerson
equalsメソッドに渡された場合は、常に を返しますfalse
。オブジェクトの種類を確認するにはどうすればよいでしょうか? そうです、instanceof
演算子を使用することです。
新しいコードは次のようになります。
コード | 説明 |
---|---|
|
参照の比較 渡されたオブジェクトはオブジェクト null ですか? 渡されたオブジェクトが Person |
4. 2 つのPerson
オブジェクトの比較
結局私たちはどうなったのでしょうか?メソッドの最後に到達すると、 でPerson
はないオブジェクト参照が存在しますnull
。したがって、それを に変換しPerson
、両方のオブジェクトの関連する内部データを比較します。これが4 番目のシナリオです。
コード | 説明 |
---|---|
|
参照の比較 渡されたオブジェクトはオブジェクト null ですか? 渡されたオブジェクトが型キャスト でない場合 Person |
では、2 つのオブジェクトをどのように比較するのでしょうかPerson
? name
名前 ( ) と年齢 ( 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 番目のシナリオのコードは次のようになります。
|
年齢が等しくない場合は、 すぐに が等しい return false 場合、このメソッドを使用して比較する意味はありません。ここでは、2 番目のフィールドが に等しいか、そうでないかのどちらかです。 メソッドを使用して 2 つの名前フィールドを比較します。 this.name null equals name null equals |
5.hashCode()
方法
両方のオブジェクトのすべてのフィールドの詳細な比較を実行することを目的としたメソッドに加えてequals
、不正確ではあるが非常に迅速な比較に使用できる別のメソッドがありますhashCode()
。
数千の単語のリストをアルファベット順に並べ替えているときに、単語のペアを繰り返し比較する必要があると想像してください。そして単語は長く、たくさんの文字で構成されています。一般に、このような比較には非常に長い時間がかかります。
しかし、加速することは可能です。異なる文字で始まる単語があるとします。それらが異なることはすぐに明らかです。しかし、それらが同じ文字で始まる場合、結果がどうなるかはまだわかりません。単語が同じであることが判明する場合もあれば、異なることが判明する場合もあります。
このhashCode()
方法も同様の原理を使用して機能します。オブジェクトに対してこれを呼び出すと、単語の最初の文字に似た数値が返されます。この数値には次の特性があります。
- 同一のオブジェクトは常に同じハッシュコードを持ちます
- 異なるオブジェクトが同じハッシュコードを持つことも、異なるオブジェクトのハッシュコードを持つこともできます
- オブジェクトのハッシュコードが異なる場合、オブジェクトは明らかに異なります。
これをさらに明確にするために、これらのプロパティを言葉で再構成してみましょう。
- 同じ単語は常に同じ最初の文字を持ちます。
- 異なる単語の最初の文字が同じであることも、最初の文字が異なることもあります
- 単語の最初の文字が異なる場合、その単語は明らかに異なります
最後のプロパティは、オブジェクトの比較を高速化するために使用されます。
まず、2 つのオブジェクトのハッシュコードが計算されます。これらのハッシュコードが異なる場合、オブジェクトは明らかに異なるため、それ以上比較する必要はありません。
ただし、ハッシュコードが同じ場合でも、equals メソッドを使用してオブジェクトを比較する必要があります。
6. コードでの契約
上記の動作は、Java のすべてのクラスで実装する必要があります。コンパイル中に、オブジェクトが正しく比較されているかどうかを確認する方法はありません。
Java プログラマは、equals() メソッドの独自の実装を記述して (クラス内の) 標準実装をオーバーライドする場合、前述の規則が満たされるような方法でメソッドObject
の独自の実装も記述しなければならないという共通の合意を持っています。hashCode()
満足。
この取り決めは契約と呼ばれます。
equals()
クラス内に のみ、または メソッドのみを実装した場合hashCode()
、契約に重大な違反を犯したことになります (契約に違反したことになります)。こんなことはしないでください。
他のプログラマーがあなたのコードを使用すると、正しく動作しない可能性があります。さらに、上記の規約の遵守に依存するコードを使用することになります。
要素を検索するとき、すべての Java コレクションはまずオブジェクトのハッシュコードを比較し、その後でメソッドを使用して比較を実行しますequals
。
つまり、独自のクラスにequals
メソッドを与えても、独自のメソッドを記述していない場合hashCode()
、またはメソッドの実装が間違っている場合、コレクションはオブジェクトで正しく動作しない可能性があります。
たとえば、リストにオブジェクトを追加し、メソッドを使用してそれを検索してcontains()
も、コレクションでオブジェクトが見つからない可能性があります。
GO TO FULL VERSION