ハッシュとは何ですか?
ハッシュは一般に「ハッシュ」として変換されます。「ハッシュ」として直接音訳があります。これは、ハッシュアルゴリズムを介して任意の長さの入力を固定長の出力に変換し、出力はハッシュ値(ハッシュ値)です。 ;この変換は圧縮マップです。つまり、ハッシュ値のスペースは通常入力のスペースよりもはるかに小さいため、異なる入力が同じ出力にハッシュされる可能性があるため、ハッシュ値から入力値を一意に決定することは不可能です。簡単に言えば、任意の長さのメッセージを特定の長さのメッセージダイジェストに圧縮する機能です。
すべてのハッシュ関数には、次の基本的な特性があります。同じハッシュ関数に従って計算されたハッシュ値が異なる場合、入力値も異なる必要があります。ただし、同じハッシュ関数から計算されたハッシュ値が同じである場合、入力値は必ずしも同じではありません。
ハッシュ衝突とは何ですか?
2つの異なる入力値が同じハッシュ関数に従って同じハッシュ値を計算するとき、それを衝突(ハッシュ衝突)またはハッシュ衝突と呼びます。
HashMap
データ構造
Javaでは、データを保存するための2つの比較的単純なデータ構造、配列とリンクリストがあります。配列の特性は次のとおりです。アドレス指定が簡単、挿入と削除が難しい、リンクリストの特性は、アドレス指定が難しいが挿入と削除が簡単です したがって、配列とリンクリストを組み合わせて2つを活用し、チェーンアドレスメソッドと呼ばれる方法を使用してハッシュの競合を解決します。
これにより、同じハッシュ値を持つオブジェクトをリンクリストに編成できます。hash
値に対応するbucket
低いが、それに比べてhashCode
リターンint型、我々のHashMap
初期容量の大きさはDEFAULT_INITIAL_CAPACITY = 1 << 4
(すなわち、第4の辺16 2)よりもはるかに小さいint
、我々は単に使用する場合は、型の範囲hashCode
取る対応得るためにIをbucket
このハッシュの衝突の確率が大幅に増加し、最悪の場合HashMap
は単一リンクリストになるためhashCode
、特定の最適化を行う必要があります。
hash()
機能
上記の問題は主にhashCode
、残りを取ると、操作に参加するのと同等の唯一のhashCode
下位ビット、上位ビットは役割を果たさないため、hashCode
値の上位ビットも操作に参加させて、さらに削減することを目的としていますhash
衝突の確率により、データの分布がより均一になります。この操作を摂動と呼びます。JDK 1.8
のhash()
関数は次のとおりです。
static final int hash(Object key) {
int h;
// 与自己右移16位进行异或运算(高低位异或)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
これは、JDK 1.7
より簡潔です。1.7の4ビット演算と比較すると、1.8では5つのXOR演算(9摂動)、1ビット演算と1つのXOR演算(2外乱)。
JDK1.8
赤黒木を追加
私たちがしている時には、上記アドレスチェーン方式(ハッシュテーブルを使用して)、より均等に分散我々のデータを作るために私たちの成功に機能障害、ハッシュ衝突の削減、スルーが、HashMap
大量のデータの存在下で、我々は1を追加しbucket
、対応するリストを持っているn
要素をその後の時間複雑である横切るO(n)
、この問題に対処するために、JDK 1.8
にHashMap
さらにトラバース複雑に減少行う、新しい赤黒木データ構造O(logn)
。
まとめ
ハッシュの競合を効果的に解決するためにHashMapが使用するメソッドの概要:
- チェーンアドレス方式(ハッシュテーブルを使用)を使用して、同じハッシュ値を持つデータをリンクします。
- 2回の摂動関数(ハッシュ関数)を使用して、ハッシュの衝突の確率を減らし、データ分布をより均一にします。
- 赤黒木を導入すると、走査の時間の複雑さがさらに減り、走査が速くなります。