私たちは、多くの場合、これが理由です、オーバーライドはhashCodeメソッドをオーバーライドする必要があります方法に等しいことを聞くことがありますか?すべてのJavaオブジェクトのクラスは、直接ソースオブジェクトに、サブクラスであり
1 / * 2 *著作権(C)1994、2012、Oracleおよび/またはその関連会社。全著作権所有。 3 * Oracle独自/ CONFIDENTIAL。使用して、ライセンス条項に従うものとします。 4 * 5 * / 6 7 パッケージされるjava.lang。 8 9 / ** 10個の *クラス{ @codeのオブジェクト}クラス階層のルートです。 11 *すべてのクラスは、{有する@codeのスーパークラスとしてオブジェクトを}。すべてのオブジェクトは、 12 の配列を含む*、このクラスのメソッドを実装します。 13 * 14 * @author unascribed 15 * @see のjava.lang.Class 16 * @sinceの JDK1.0 17 * / 18 パブリック クラスオブジェクト{ 19 20 プライベート 静的 ネイティブ ボイドRegisterNativesを()。 21 静的{ 22 RegisterNativesを()。 23 } 24 公衆 最終 ネイティブクラス<?> のgetClass()。 25 26 27 公衆 ネイティブ int型のハッシュコード(); 28個の 29 30 パブリック ブール等しい(オブジェクトobj){ 31 リターン(この == OBJ)。 32 } 33 34 35 保護された ネイティブオブジェクトクローン()はスローCloneNotSupportedExceptionと、 36 37 公共の文字列のtoString(){ 38の リターンのgetClass()のgetName()+ "@" +。Integer.toHexString(ハッシュコード())。 39 } 40 41 公衆 最終 ネイティブ ボイド)(通知します。 42 43 44 公衆 最終 ネイティブ ボイドのnotifyAll(); 45 46 47 公共の 最終 ネイティブ 無効待機(長いタイムアウト)がスローInterruptedExceptionあるが、 48 49 50 公衆 最終 ボイド待ち時間(長いタイムアウト、int型の nanos値)はスローInterruptedExceptionある{ 51 であれば(タイムアウト<0 ){ 52 投 新しいはIllegalArgumentException( "タイムアウト値が負の" )。 53 } 54 55 であれば(nanos値<0 || nanos値> 999999 ){ 56 スロー 新しい例外:IllegalArgumentException( 57 「範囲外ナノ秒のタイムアウト値を」); 58 } 59 60 であれば(nanos値> 0 ){ 61 タイムアウト++ 。 62 } 63 64 、待機(タイムアウト)。 65 } 66 67 公衆 最終 ボイド待機()スローInterruptedExceptionある{ 68 ウェイト(0 )。 69 } 70 71 保護 ボイドファイナライズ()はスロー }のThrowableを{
まず、ハッシュアルゴリズムとハッシュマップの下でのレビューをしましょう
長さN(ArrayListのを想定)表線形(10,000と仮定して)、デジタル記憶装置が乱れ、我々は指定された数を探している場合は、最初から最後まで目を通すしなければならない、トラバース、そのようなN(ここでは、5000)2で割って平均数を検索します。
(ここではハッシュテーブルは純粋に概念的なデータ構造である、とJavaは何の関係もありません)私たちは、ハッシュテーブルを観察してみましょう。1に近い、平均検索時間、コストがキーは、ハッシュテーブルに格納されたデータであり、その保管場所をハッシュ関数に関連付けられている、非常に小さいです。
私たちは、ハッシュ関数は、x * xの5%であると仮定します。ハッシュテーブルは、リニアテーブルの長さが11である一方、もちろん、このような単純なハッシュ関数を使用することは不可能現実には、我々は、説明の便宜のために純粋にここにいます。6私たちは、その後、我々は最初の6ハッシュ関数の計算を使用しますが、それに入れたい場合は、結果は私たちはインデックス番号が6 1この位置であげるよ、1です。私たちは数7を入れたい場合は同様に、ハッシュ関数計算の後、結果は7 4、それがインデックスに含まれますされ、4位です。図に示すように、結果。
これの利点は明らかです。例えば、我々は、この要素6から見つけ、我々は、インデックスは、第1のハッシュ関数の位置6で算出することができ、そして、インデックスにおける1番から直接それを見つけます。
しかし、私たちは、「ハッシュ値の衝突、」この問題が発生しました。このような計算のハッシュ関数の後、図7および図8は、JavaのHashMapオブジェクトを使用して同一のハッシュ値が、「ストランドアドレス方式」ソリューションである持っています。図に示すように結果。
すべてのハッシュ値iは同義語リストを作成オブジェクトであるため具体的なアプローチがあります。それはリンクリスト8に新しいノードを作成します、我々は会計処理されている8ディスカバリー4位に時間を置くとします。私たちは8を探している場合も同様に、その後、4番のインデックスの代わりに、8、およびダウンリストターン表情でその意志で見つかりました。
我々はまだ完全にハッシュ値の競合の問題を回避することはできませんが、ハッシュ関数の設計が合理的であるが、それはまだ同義語リストの長さは、合理的な範囲で制御されていることを確認することができます。理論的知識といえばランダムではありません、我々は明らかに後年における重要書き換えhashCodeメソッドを理解することができます。
簡単な例
私たちは、HashMapのでカスタムクラスを堆積するとき、彼らはクラス定義からのequalsとhashCodeメソッドをオーバーライドしていない、結果は我々が期待通りに同じではないだろう場合。私たちは、WithoutHashCode.javaこの例を見て。
第2ライン18において、我々は、クラスキーを定義し、行3に一意の属性IDを定義します。現在、我々は最初の行9件のequalsメソッドで、ライン16上でhashCodeメソッドをコメントしています。
1 インポートjava.util.HashMapを、 2 クラスキー{ 3 プライベート整数ID; 4 パブリック整数のgetId() 5 { 戻りID;} 。6 パブリックキー(整数ID) 7 {。この .ID = ID;} 。8 // 意図的に第一等号とhashCodeメソッドコメントアウト 。9 // パブリックブールのequals(オブジェクトO){ 10 // IF(!O == nullの||(Oキー)のinstanceof) 。11 // falseに{リターン;} 12はある // 他の 13がある // {リターンthis.getId()(((キー)O).getId())に等しい;} 14 // } 15 16 // 公共int型のハッシュコード() 17 // {リターンid.hashCode()。} 18 } 19 20 パブリック クラスWithoutHashCode { 21 公共 静的 ボイドメイン(文字列[]引数){ 22キーk1 = 新しい鍵(1 )。 23鍵K2 = 新しいキー(1 ); 24のHashMap <キー、文字列> HM = 新規のHashMap <キー、文字列> (); 25 hm.put(K1、 "IDとキーが1です" )。 26 のSystem.out.println(hm.get(K2))。 27 } 28}
それらが同じ2つのキーであるように第22及びライン23の主な機能では、我々は、同じドアを開くことができ、そのIDが1である二つの重要なオブジェクトを定義します。
24行目では、ジェネリック医薬品によるHashMapのオブジェクトを作成します。これは、オブジェクトキー部キータイプ、値を格納することができ、String型のオブジェクトに格納することができます。
ライン25には、我々はk1とHMに入れて文字列による方法を入れて、そしてライン26には、我々は、HashMapのk2のレーンから値を取得したい。我々はK1にこのキーを使用するようなものですドアをロックし、K2とのドアに来ました。これは論理的であるが、現在の結果は、文字列の結果は、我々は26行が、ヌルを想像するものではありません返します。
書き換えられていない - 2つの理由があります。hashCodeメソッドをオーバーライドしていない最初は、何のオーバーライドが第二の方法に等しくないがあります。
我々はHashMapのにK1を入れると、hashCodeメソッドは、まずメモリ位置によって導かK1のハッシュ値に入れて、このクラスのキーは、そのハッシュ値を計算する呼び出します。
キーは、我々がhashCodeメソッドキーを定義していないです。ここで、まだオブジェクトクラス(すべてのクラスオブジェクトのサブクラスである)hashCodeメソッドを呼び出して、クラスオブジェクトhashCodeメソッドは、ハッシュ値を返し、実際に(1,000とする)メモリ・アドレス・K1・オブジェクトです。
私たちは、呼び出しは(K1)をhm.get、その後、我々はhashCodeメソッド(K1または1000のリターンアドレス)を再度呼び出します続く場合は、ハッシュ値に応じて得られるk1はすぐに見つけることができます。
しかし、ここで計算されたハッシュ値K2(定義されていないキーのように)、実際には、K2のメモリアドレスは(2000とする)を取得するとき、我々はObjectクラスのhashCodeメソッドを呼び出して、我々はhm.get(K2)されているコードは、です。K1およびK2は、2つの異なるオブジェクトなので、それは我々がK1とK2のハッシュ値を取得することはできません理由です、そのハッシュ値が異なっていなければならないことを意味し、同じメモリアドレス、ではありません。
我々は、行16と17の除去hashCodeメソッドを注釈付きの場合、そのハッシュ値が等しいので、それは、k1とk2は1のIDであるid属性のハッシュコードの戻り値であることがわかります。
私たちはK1およびK2テイクアクションを保ったものを修正してみましょう。場合預金k1は、そのIDのハッシュ値に基づいて、対応する位置に、ここでK1オブジェクト100を想定しています。K2を服用中(IDが1であるため、この値が100であるK2)を、それがハッシュ値を計算することで、この位置に行きます。
しかし、結果は私たちの驚きになります:第100位は明らかにK1を持っていますが、出力ライン26はまだnullです。その理由には、オブジェクトがメソッドキーの書き換えに等しくないということです。
ハッシュマップがある競合チェーンアドレス法によって処理され、位置100で、リンクされたリストに格納されたオブジェクトが複数存在してもよいです。彼らは、ハッシュ値が100 hashCodeメソッドによって返されます。
我々はK2の100位にハッシュコードに目を通すときは、get k1はありません。しかし、K1およびK2は、同じハッシュ値を持つことも可能であるが、必ずしもそうではないに等しく、およびK2(K1とK2 2つのキーが同じドアを開くことができない場合があります)、この時間は、我々は2かどうかを判断するために等しいメソッドKeyオブジェクトを呼び出す必要があります等しいです。
我々はキーオブジェクトのequalsメソッドを定義していないので、システムは、Objectクラスのequalsメソッドを呼び出す必要があります。K1とK2は等しくならないようによるオブジェクトの固有の方法にはまだヌルに理由を与える(K2)hm.get介してライン26に残っている理由である、2つのオブジェクトのメモリアドレスに応じて判断されます。
オーバーライドが等しいなぜ一般的方法は、hashCodeメソッドをオーバーライドする必要があります
まず、上記の整数のソースを見て
@Override 公共 int型のハッシュコード(){ 戻りInteger.hashCode(値)。 } パブリック 静的 int型のハッシュコード(INTの値){ 戻り値。 } パブリック ブール等しい(オブジェクトobj){ 場合(OBJ のinstanceof 整数){ 戻り値== ((整数)OBJ).intValue(); } を返す 偽。 }
Integerクラスは、ハッシュコードメソッドIntegerクラスが戻り値そのものであり、それ自体が等しい比較手法の値と等しい、方法およびハッシュコード方法は、書き換え可能であるに等しいです。
equalsメソッドは、次の特性を満たす必要があります。
1.再帰:x.equals(X)== trueの場合、自分自身と比較等しいです
2.対称性:x.equals(Y)== y.equals(x)は、2つのオブジェクトが呼び出しは、結果が同じでなければならないに等しいです
3.推移:x.equals(Y)==真y.equals(Z)== trueの場合、x.equals(Z)== trueの場合、およびxがyに等しい場合、YおよびZは同じであり、XおよびZは、に等しいです。
4.整合性:xとyターゲットオブジェクトは、メンバ変数NUM1とNUM2を有するオーバーライドのみNUM1動作に参加する方法に等しい場合、修正はNUM2のx.equals(y)の値に影響を及ぼしませんの
クラスは、ハッシュコードメソッドをオーバーライドしない場合、次いで、その後、2つの決意値が同じであるが、値は、このような曖昧さを引き起こすStringクラスとして、同じハッシュコードはないに等しいです