HashMapに関するいくつかの考え

1.HashMapの負荷率の役割

HashMapの要素(リンクリストの要素と赤黒木を含む)の数が配列の長さの0.75倍に達すると、展開が開始されます。

次に、HashMapの負荷率が0.75である理由

主な目的は、スペース使用率を改善し、クエリコストを削減することです(ハッシュの競合を可能な限り削減するとも言えます)。

第三に、スロットの数が2 ^ nを使用しなければならない理由

ハッシュ結果の分布をより均一にしたい場合、最初に頭に浮かぶのは、剰余(%)演算を使用することです。重要な点は次のとおりです。「剰余(%)演算では、除数が2の累乗の場合、AND(&)演算と同等です(つまり、ハッシュ%長さ==ハッシュ&(長さ-1))前提は、長さが2のn乗であるということです。」そして、%に対してバイナリビット演算&を使用すると、計算の効率を向上させることができます。これは、HashMapの長さが2の累乗である理由を説明しています。

第四に、ハッシュの競合を解決する方法

1.オープンアドレス法

:: Fi(キー)=(f(キー)+ di)MOD m(di = 0,1,2,3、......、m-1)

キー:配列に配置される要素(ハッシュテーブル); m:配列の長さ

競合が発生すると、特定の検出手法を使用してハッシュテーブルに検出シーケンスが形成されます。指定されたキーワードが見つかるまで、またはオープンアドレス(つまり、アドレスユニットが空)に遭遇するまで、このシーケンスに沿ってユニットごとに検索します(挿入する場合は、オープンアドレスが検出されたときに、新しいアドレスを挿入できます。ノードはアドレス単位に格納されます)。検索中にオープンアドレスが検出された場合は、テーブル内に検索するキーワードがない、つまり検索が失敗したことを示しています。

(1)線形検出法

アイデアは次のとおりです。数式を使用して配列内の要素の添え字を計算します。添え字に要素がない場合は直接入力します。添え字に要素がある場合、数式のdiは+1で再計算されます。要素が見つからなくなるまで、下付き文字を回します。それ以外の場合、アレイはいっぱいであり、拡張する必要があります。

(2)2番目の検出方法

アイデアは次のとおりです。diの計算方法を変更して要素なしで添え字をクエリするには、具体的な計算方法はdi = -12,12、-22,22、...、-(q * 10 + 2)、(q * 10 + 2)、q <= m / 2。diの値については、まだ調べていませんので、コピーしましたが、この検出方法の考え方を知っておく必要があります。

考慮される状況は、下付き文字が式によって計算された後のすべての下付き文字が要素で占められており、この下付き文字の前に空きがある場合、最初の方法で計算できますが、計算の数が比較的多いという状況です。 。この方法は、計算の数を減らすことができます。

(3)疑似乱数の検出とハッシュ

考え方は次のとおりです。diの値はランダム関数を介して取得されます。ランダム関数のシードが同じである場合、取得されたdiも同じであり、クエリは問題ありません。

つまり、オープンアドレス法では、ハッシュテーブルがいっぱいにならない限り、競合しないアドレスを常に見つけることができます。これは、競合を解決するための一般的な方法です。

2.ジッパー方式

つまり、ハッシュの競合が発生すると、競合するノードにリンクリストが形成されます。HashMapは、zipperメソッドによって解決されるハッシュの競合です。

5.リンクリストの長さが8に達すると、リンクリストが赤黒木に変わるのはなぜですか。

負荷率として0.75を使用する場合、リンクリストの長さが8に達することはほとんど不可能です。戦略のバランスを取ります。

HashMapソースコードのコメントを引用します。

* 0:0.60653066 * 1:0.30326533 * 2:0.07581633 * 3:0.01263606 * 4:0.00157952 * 5:0.00015795 * 6:0.00001316 * 7:0.00000094 * 8:0.00000006 *多い:1,000万分の1未満

6. HashMapの展開中に要素の位置はどうなりましたか?

3つの状況に分けられます:

  • 配列の要素の場合:計算されたハッシュ値を直接使用して新しい添え字を再計算し、それを新しい配列に配置します。
  • リンクリストの場合:リンクリストを2つに分割すると、配列の長さよりも大きいハッシュ値を持つ新しいリンクリストが新しい配列に配置され、配列の長さよりも短いハッシュ値を持つものが配置されます。元の配列で。
  • 赤黒木について:数値を2つのリンクリストに分割し、配列の長さよりも大きいハッシュ値を持つ新しいリンクリストを新しい配列に配置し、長さよりも大きいハッシュ値を持つ新しいリンクリストを配置します。最後に、2つのリンクリストを赤黒木に変換する必要があるかどうかを再判断します。

キーコード:

do { 
    next = e.next; if((e.hash&oldCap)== 0){if(loTail == null)
            loHead = e; else 
            loTail.next = e; 
        loTail = e; 
    } else {if(hiTail == null)
            hiHead = e; else 
            hiTail.next = e; 
        hiTail = e; 
    } 
} while((e = next)!= null);

例:oldCapが16の場合、展開後の新しい配列の長さは32であり、リンクリストの要素はそれぞれ7、23、39です。(整数のハッシュ値はそれ自体です)

  7:0000 0111 
&16:0001 0000 
--------------- 
 =:0000 0000#0、元の位置のまま 
  17:0001 0001 
&16:0001 0000 
----- ----------- 
 =:0001 0000#0ではなく、[ 
  17、32 23:0001 0111
と16:0001 0000 
-------------の間に配置する必要があります - - 
 =:0001 0000#ない0、[17、32)との間に配置する必要が 
  39:0010 0111 
&16:0001 0000 
--------------- 
 = 0000 0000#0 、その値が配列の長さよりも大きいため、まだ配置されています



おすすめ

転載: blog.51cto.com/15138908/2676975