機能:追加、削除、チェックの時間の複雑さはすべてO(1)です。
ストレージKEYはNullおよび一意にすることができ、値はNULLにすることができます。容量拡張(初期サイズは16(2の累乗)、負荷係数は0.75、容量拡張は元のサイズの2倍です)。
概念:妨害関数(ハッシュ関数):ハッシュ関数を使用して、不適切なhashCode()の使用を回避し、ハッシュの衝突の発生を減らします。
JDK1.7
データ構造:配列とリンクリスト(一方向);スレッドの安全ではない、マルチスレッドの無限ループが発生します。
配列とリンクリストの構造:HashMapの内部エントリで構成される配列;配列の添え字の計算:自身で実装されたハッシュ()アルゴリズム、およびXOR演算を実行し、対応する配列の添え字の位置を取得するための係数を取得;ハッシュアルゴリズムの記述利点は、ハッシュ衝突の発生が減少することです。
コードは次のとおりです。キーのhashCode値、9回の摂動処理= 4回のビット演算+ 5回のXOR。
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
リンクリストはヘッド挿入方式を採用しています。マルチスレッド同時実行の場合、展開プロセス中に、transfer()メソッドは古いリンクリストをループしてから、新しいリストヘッダーを再度ポイントします。1つのスレッドが1つのスレッドを中断して実行を続行し、最終的に2つのスレッドになりますデータポイントを相互に挿入して、循環リンクリストを形成します。配列内のデータを取得すると、無限ループが発生します。
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
// 如多线程执行时,某一线程执行到该步骤挂起,则会导致上述所说的环形链表的产生。
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
頭部補間法によるデッドループに加えて、リンクリスト構造には重大な問題があります。ハッシュの衝突が多数発生すると、多数のノードがエントリに格納されます。一方向リンクリストにより、データ検索(シーケンシャル検索)が検索に大きく影響します。効率。
JDK1.8
データ構造:配列、リンクリスト、赤黒木。スレッドは安全ではなく、末尾の補間は無限ループにはなりません。
キーのhashCode値、2摂動処理= 1ビット演算+ 1 XOR。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
リンクリストはテール補間を使用します。これは無限ループを引き起こしませんが、マルチスレッドの場合、データの損失を引き起こす可能性があります。
リンクリストを赤黒木に変換するためのしきい値は8です。
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;
...
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
...
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash); //转化为红黑树
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
...
}
問題は、7未満の場合はリンクリストに変換され、7を超える場合は赤黒ツリーに変換される理由です。
まず、赤黒木は必ずしもリンクリストよりも効率的ではありません。ノード数が多い場合は、赤黒ツリーの方が効率的です。6と8を選択します(リンクリストが6本以下の場合は、リンクリストに復元し、8以上でツリーに復元します)。リンクリストとツリーの頻繁な変換を効果的に防止するために、中央に7の違いがあります。ハッシュバケットに分散されるコンテナー内のノードの頻度はポアソン分布に従い、バケットの長さが8を超える確率は非常に小さくなります。