HashCode()およびEquals()メソッドは、Javaの完全にオブジェクト指向の主要な機能であると言えます。それは私たちのプログラミングを促進し、また多くの危険をもたらします。この記事では、これら2つの方法を正しく理解して使用する方法について説明します。
equals()メソッドを書き換えることにした場合、そうすることによってもたらされるリスクについて明確にし、堅牢なequals()メソッドを記述できることを確認する必要があります。注意しなければならないことの1つは、equals()を書き換えた後、HashCode()メソッドを書き換える必要があることです。具体的な理由については後で説明します。
Javase 7仕様のequals()メソッドの説明を最初に見てみましょう。
of反射的です:非ヌルの参照値x, x.equals(x) trueを返す必要があります。
symetricです: y, x.equals(y) true返す場合にのみ、非null参照値xおよびy、 y.equals(x) trueを返す必要があります。
transitive transitive:null以外の参照値x, y, zの場合、 x.equals(y)がtrueを返し、 y.equals(z)がtrueを返す場合、 x.equals(z)はtrue.
・一貫しています:null以外の参照値xの場合y 、 x.equals(y)の複数の呼び出しは、一貫してtrueを返すか、一貫してfalseを返します。
null以外の参照値x, x.equals(null) falseを返す必要があります。
この一節は、個別の数学で多くの数秘術を使用しています。簡単な説明をさせてください:
1。反射性:A.エクール(a)は真実を返す必要があります。
2。対称性:A.Equals(b)がTrueを返す場合、B.Equals(a)もtrueを返す必要があります。
3。送信:A.Equals(b)が真であり、B.Equals(c)が真である場合、A.Equals(c)も真でなければなりません。率直に言って、a = b、b = c、次にa = C。
4。一貫性:AとBオブジェクトの状態が変わらない限り、A.Equals(b)は常に真である必要があります。
5。A.Equals(null)falseを返す。
数学で専門家ではない人が上記のことを呼ばない限り、私は信じています。実際のアプリケーションでは、特定の手順に従ってequals()メソッドを書き直す必要があります。説明の利便性のために、最初にプログラマークラス(コーダー)を定義します。
class Coder {private string name;プライベートインクエイジ; //ゲッターとセッター}私たちが望んでいるのは、2つのプログラマーオブジェクトの名前と年齢が同じである場合、これら2つのプログラマーが同じだと思うということです。現時点では、そのequals()メソッドを書き直す必要があります。デフォルトは()等しい()が実際に2つの参照が同じオブジェクトを内因性に指しているかどうかを決定するため、==に相当します。書き換えるときは、次の3つの手順に従ってください。
1。それがあなた自身と等しいかどうかを判断します。
if(other == this)trueを返します。
2。instanceof演算子を使用して、他のオペレーターがタイプコーダーのオブジェクトであるかどうかを判断します。
if(!(その他のinstance of coder))falseを返します。
3. Coderクラスでカスタマイズするデータドメイン、名前、年齢を比較すると、見逃してはなりません。
Coder O =(Coder)other; return o.name.equals(name)&& o.age == age;
これを見て、誰かが尋ねるかもしれません、ステップ3にキャストがあります。誰かが整数クラスのオブジェクトをこれに等しく渡すと、彼はClassCastExceptionを投げますか?この心配は実際には冗長です。 2番目のステップでインスタンスの判断を下したため、他のオブジェクトが非コダーオブジェクトである場合、または他のオブジェクトがnullである場合、このステップでfalseが直接返されるため、後続のコードが実行される機会が得られません。
上記の3つのステップは、<Effection Java>で推奨される手順でもあります。これは、基本的に間違いがないことを保証できます。
Javase 7仕様で、
「一般に、この方法(等しい)がオーバーライドされたときはいつでもハッシュコードメソッドをオーバーライドする必要があることに注意してください。ハッシュコード法の一般的な契約を維持するためです。
equals()メソッドを書き換える場合は、HashCode()メソッドを書き換えることを忘れないでください。大学のコンピューターデータ構造コースでハッシュテーブルを学びました。 HashCode()メソッドは、ハッシュテーブルを提供します。
ハッシュマップやハッシュセットなどのハッシュのようなハッシュから始まるコレクションクラスを使用すると、ハッシュコード()は、ハッシュマッピング関係を作成するために暗黙的に呼ばれます。これについては後で説明します。ここでは、最初にHashCode()メソッドの書き込みに焦点を当てます。
<EffectiveJava>は、ハッシュの競合を最大限に回避できるライティング方法を提供しますが、個人的には、一般的なアプリケーションにそれほどトラブルを起こす必要はないと思います。アプリケーションに数万または数百万のオブジェクトを保存する必要がある場合は、本に記載されている方法に厳密に従う必要があります。中小のアプリケーションを書いている場合、次の原則で十分です。
Coderオブジェクトのすべてのメンバーがハッシュコードに反映されるようにする必要があります。
この例では、これを書くことができます。
@Override public int hashcode(){int result = 17;結果= result * 31 + name.hashcode();結果=結果 * 31 +年齢;返品結果; }int result = 17では、20、50などに変更することもできます。これを見て、私は突然興味があり、文字列クラスのHashCode()メソッドがどのように実装されているかを見たいと思いました。ドキュメントを確認して知っています。
「この文字列のハッシュコードを返します。文字列オブジェクトのハッシュコードは、
s [0]*31^(n-1) + s [1]*31^(n-2) + ... + s [n-1]
s [i]は文字列のith文字であり、nは文字列の長さであり、 ^は指数を示します。 (空の文字列のハッシュ値はゼロです。)」
各文字のASCIIコードを電源n -1に計算してから追加します。ハッシュコードの実装において太陽が非常に厳格であることがわかります。これにより、2つの異なる文字列の同じハッシュコードを最大限に回避できます。
バケットの概念は、Oracleのハッシュテーブル実装で参照されています。下の図に示すように:
上記の図からわかるように、バケット付きのハッシュテーブルは、ハッシュテーブルとリンクリストの組み合わせとほぼ同等です。つまり、各バケットにリンクされたリストが掛けられ、リンクリストの各ノードがオブジェクトを保存するために使用されます。 JavaはHashCode()メソッドを使用して、オブジェクトを配置するバケットを決定し、対応するリンクリストで検索します。理想的には、HashCode()メソッドが十分に堅牢に記述されている場合、各バケットには1つのノードのみがあり、検索操作の一定レベルの時間の複雑さが実現されます。つまり、オブジェクトがどのメモリに配置されていても、最初から最後まで通過して検索することなく、hashcode()を介してすぐに領域を見つけることができます。これは、ハッシュテーブルの主なアプリケーションでもあります。
のように:
ハッシュセットのput(object o)メソッドを呼び出すと、最初にO.hashcode()の返品値に従って、対応するバケットに配置します。バケットにノードがない場合は、ここにoを置きます。すでにノードがある場合は、リンクリストの最後にo oを掛けます。同様に、呼び出しがcontas(オブジェクトo)の場合、JavaはHashcode()の戻り値を介して対応するバケットを見つけ、次に、対応するリンクリストのノードでequals()メソッドを呼び出して、ノード内のオブジェクトが必要かどうかを判断します。
このプロセスを体験するために例を使用しましょう。
最初に2つの新しいコーダーオブジェクトを作成しましょう。
Coder C1 = New Coder( "Bruce"、10); Coder C2 = new Coder( "Bruce"、10);
HashCode()メソッドを書き換えることなく、Coderのequalsメソッドを書き換えたと仮定します。
@Override public boolean equals(object otfern){system.out.println( "equals method voked!"); if(other == this)trueを返します。 if(!(その他のinstance of coder))falseを返します。 Coder O =(Coder)other; return o.name.equals(name)&& o.age == age; }次に、ハッシュセットを構築し、C1オブジェクトをセットに入れます。
set <coder> set = new Hashset <Coder>(); set.add(c1);
もう一度実行:
system.out.println(set.contains(c2));
contains(c2)メソッドが真であると予想していますが、実際にはfalseを返します。
C1とC2の名前と年齢は同じです。 C1をハッシュセットに入れた後、contains(c2)を呼び出し、falseを返すのはなぜですか?これは、トラブルを引き起こしているハッシュコード()です。 HashCode()メソッドを書き換えていないため、HashsetがC2を検索すると、異なるバケットで探します。たとえば、C1がBucket 05に入れられると、C2を検索するときにBucket 06で検索されるため、もちろん見つかりません。したがって、hashcode()の書き換えの目的は、a.equals(b)がtrueを返すと、aとbのハッシュコード()が同じ値を返すことです。
HashCode()に毎回固定番号行を返すように依頼しますか
誰かがこのように書き直すかもしれません:
@Override public int hashcode(){return 10; }この場合、ハッシュマップ、ハッシュセット、その他のコレクションクラスは「ハッシュの意味」を失います。 <効果的なJava>の言葉では、ハッシュテーブルはリンクされたリストに変性します。 HashCode()が毎回同じ番号を返す場合、すべてのオブジェクトが同じバケツに配置され、検索操作を実行するたびにリンクリストを通過し、ハッシュの機能が完全に失われます。したがって、良いアイデアとして堅牢なハッシュコード()を提供することをお勧めします。
上記は、HashCode()およびEquals()メソッドの書き換えに関するこの記事のすべての詳細な紹介です。私はそれが誰にでも役立つことを願っています。興味のある友人は、このサイトの他の関連トピックを引き続き参照できます。欠点がある場合は、それを指摘するためにメッセージを残してください。このサイトへのご支援をありがとうございました!