ほとんど顔の質問は尋ねなければなりません
インタビューの中で主要なJava開発、依頼することが多い質問は:あなたはオーバーライドされているかhashcode
の方法を?多くの候補者が直接書いた言いませんでした。あなたは、問題によって確認することができますので、多分何かが、書かれていません:あなたはHashMapのキー(としているKey
)部分、何のletカスタムオブジェクトが存在しませんか?そして、この時間は、彼らは自己矛盾した上に2つの質問に答え、候補者が言ってみましょう。
実際には、多くの人々は、単にので、この記事では、一般的に良いこの質問に回答する必要がありませんから、hash
テーブルトーク私たちは自然にこれらの質問に対する答えを知っていることにより、データのHashMapのルールの存在について、。
ハッシュアルゴリズムを乗り越えます
知識のデータ構造確認するには:長さn
(と仮定し10000
、障害のデジタルストレージを(つまり、ArrayListにあると仮定して)テーブルの直線)を、我々は指定された番号を探しているなら、あなたは最初から通過する必要があります尾を見つけるためにトラバースします。
(ここではハッシュテーブルは純粋に概念的なデータ構造である、とJavaは何の関係もありません)私たちは、ハッシュテーブルを観察してみましょう。これは、数平均検索の近くに位置してい1
たキーがデータをそこに格納し、その格納位置をハッシュ関数に関連付けられている、ハッシュテーブルであり、コストが非常に小さいです。
私たちは、関数があるハッシュを想定します。もちろん、現実には不可能簡単なハッシュ関数で、ここでは純粋に説明の便宜上、ハッシュテーブルの長さがあるリニアテーブル。私たちがしたい場合は、それに入れ、その後、我々は最初のでしょう、ハッシュ関数の計算結果があるので、我々はよインデックス番号がつけ、この位置。我々は数字を入れたい場合は同様に、ハッシュ関数計算の後に、結果がされ、それがインデックスに含まれますされ、この位置に。図に示すように、結果。x*x%5
11
6
6
1
6
1
7
7
4
4
これの利点は明らかです。たとえば、私たちはから探している6
、この要素は、我々は最初のハッシュ関数により算出した6
インデックス位置、および、直接の1
インデックス番号でそれを見つけます。
しかし、私たちは、「ハッシュ値の衝突、」この問題が発生しました。例えば、ハッシュ関数を計算し、後7
および8
JavaのHashMapのオブジェクトを使用するのと同じハッシュ値である持っている「鎖アドレス方式」溶液。図に示すように結果。
具体的なアプローチは、すべてのハッシュ値であるi
同義語リストを作成するためのオブジェクト。我々は中に入れていると8
時間、見つかった4
ん場所は、それがリンクリストに新しいノードを作成し、計上されていません8
。我々が探している場合は同様に、8
[検索、4
インデックス内の数字はないが8
、それは、リストダウン表情に変わります。
我々はまだ完全にハッシュ値の競合の問題を回避することはできませんが、ハッシュ関数の設計が合理的であるが、それはまだ同義語リストの長さは、合理的な範囲で制御されていることを確認することができます。理論的知識といえばランダムではありません、我々は明らかに後で理解できる重要書き換えhashCodeメソッド。
オーバーライドイコールとhashCodeメソッドへの髪のために
私たちは、HashMapのでカスタムクラスを堆積するとき、彼らはクラス定義からのequalsとhashCodeメソッドをオーバーライドしていない、結果は我々が期待通りに同じではないだろう場合。で見てみましょう。この例。WithoutHashCode.java
前記第2
二の18
行、我々は定義するKey
クラスと、第1た3
行は、一意のプロパティを定義しますid
。現在、我々は最初の最初のコメントアウト9
の行equals
法と16
ラインhashCode
方法を。
-
1 import java.util.HashMap;
-
2 class Key {
-
3 private Integer id;
-
4 public Integer getId()
-
5 { return id; }
-
6 public Key(Integer id)
-
7 { this.id = id; }
-
8 //故意先注释掉equals和hashCode方法
-
9 // public boolean equals(Object o) {
-
10 // if (o == null || !(o instanceof Key))
-
11 // { return false; }
-
12 // else
-
13 // { return this.getId().equals(((Key) o).getId());}
-
14 // }
-
15
-
16 // public int hashCode()
-
17 // { return id.hashCode(); }
-
18 }
-
19
-
20 public class WithoutHashCode {
-
21 public static void main(String[] args) {
-
22 Key k1 = new Key(1);
-
23 Key k2 = new Key(1);
-
24 HashMap<Key,String> hm = new HashMap<Key,String>();
-
25 hm.put(k1, "Key with id is 1");
-
26 System.out.println(hm.get(k2));
-
27 }
-
28 }
ではmain
最初の関数22
と23
行、我々は2定義しKey
たオブジェクトを、彼らがid
している1
彼らが同じ2つのキーが同じドアを開けることができているように、。
最初に24
行、我々はジェネリックでHashMapのオブジェクトを作成しました。その主要部分は保存することができるKey
オブジェクトのタイプを、オブジェクトは、String型の値の部分を格納することができます。
最初に25
行、我々は合格put
法k1
と入れて文字列hm
にすると、第1に26
ライン、我々は望んk2
ではHashMapから値を取得するために、私たちが使いたいようなものだk1
、ドアをロックするために使用し、このキーをk2
ドアを開きます。これは論理的であるが、現在の結果が、26
結果は我々は、行の文字列と想像が、何ではありません返しますnull
。
2つの理由があります。まず、hashCodeメソッドをオーバーライドしていないが、第二は、equalsメソッドを書き換えることではありません。
我々はHashMapのに入れたときk1
の時間、それは最初に呼び出しますKey
クラスhashCode
の計算方法hash
値を、次に置くk1
メモリ位置によって導かれたハッシュ値に。
キーは、私たちが持っていないですKey
の定義にhashCode
する方法。ここでは、コールはまだObject
クラスのhashCode
メソッド(すべてのクラスがObjectのサブクラスです)、およびObject
クラスhashCode
メソッドの戻りhash
値は、実際k1
のオブジェクト内存地址
(1000であると想定します)。
我々は通話に従った場合、我々は再び呼び出されますメソッド(またはリターンアドレス)後、得られたに基づいた値、それはすぐに見つけることができます。hm.get(k1)
hashCode
k1
1000
hash
k1
しかし、私たちはここにあるコードは、我々が呼ぶとき、クラス(のような方法を計算するために定義されていない)で、実際には、値を取得したメモリアドレスを(あると仮定)。以来と2つの異なるオブジェクトである、彼らは彼らで同じメモリアドレス、ではありません、これは私たちが使用できないものです、値が異なっていなければならない価値が選ぶ理由。hm.get(k2)
Object
hashCode
Key
k2
hash
k2
2000
k1
k2
hash
k2
hash
k1
我々は最初のとき16
と17
行hashCode
注釈メソッドの後に削除するために、あなたはそれを返すでしょうid
属性hashCode
値、どこにk1
、とk2
しているid
彼らのように、両方の1 hash
の値が等しいです。
私たちはどのような預金修正しようk1
とテイクk2
アクション。デポジットk1
それが基づいている値は、ここで想定し、オブジェクトが対応する位置に配置されます。そして取る、それが最初に計算された時間を、(原因値にはあり、この値である)、この位置にアクセスしてください。id
hash
100
k1
k2
hash
k2
id
1
100
明らかに:しかし、結果は私たちの驚きになる100
数の場所がすでに持っているk1
が、最初の26
出力ラインが残りますnull
。その理由は、何が上書きされることはありませんKey
、オブジェクトのequals
メソッドを。
HashMapのリンクアドレス方式の競合を処理するためには、すなわち、100
番号位置は、リンクされたリストに格納されたオブジェクトが複数存在してもよいです。そのことにより、hashCode
メソッドの戻りhash
値は100です。
我々が通過するときk2
にhashCode
まで100
、時間位置番号検索あなたは入手できますかk1
。しかし、k1
そこだけであってもよく、k2
同じ持っているhash
価値を、必ずしも必要ではないが、とk2
等しく(k1
およびk2
2つのキーが同じドアを開くことができない場合があります)、この時間は、我々は呼び出す必要がありKey
、オブジェクトのequals
2が等しいAであるかどうかを決定する方法を。
私たちは以来Key
のオブジェクトを定義しないequals
方法、システムがコールする必要がありますObject
クラスのequals
メソッドを。起因するObject
固有のアプローチは、二つのオブジェクトに基づいてメモリアドレスの判断に、k1
そしてk2
確かにそれはまだある理由である、と等しくない26
ことにより、ラインまだ取得理由を。hm.get(k2)
null
この問題を解決するために、我々は最初に開く必要がありますライン注釈方法を。この方法では、限りオブジェクトが2つであるようなタイプが、それらは等しく、それらは同じです。9
14
equals
Key
id
再び
ので、プロジェクト以来、頻繁に使用HashMapのだったインタビューは、ほぼ確実に質問をします、あなたは、オーバーライドされています:hashCode
メソッドを?HashMapの使用しているときは、書き換えたのhashCode
とequals
方法を?あなたはどのように書くのですか?
最後に、再び:あなたがオブジェクトのHashMapの「キー」セクションでは、カスタムが含まれている場合は、それ自身では、このオブジェクトを使用してくださいequals
とhashCode
オーバーライドするメソッドObject
と同じ名前のメソッドインチ