HashMap
には2つの重要なプロパティがあります:size
とload factor
。私はJavaのドキュメントを読みましたが、0.75f
が最初の負荷係数であると言っています。しかし、私はそれの実際の使用法を見つけることができません。
誰かが負荷率を設定する必要があるさまざまなシナリオと、さまざまなケースに対するサンプルの理想値とは何かを説明できますか。
ドキュメンテーション はそれをかなりよく説明しています:
HashMapのインスタンスには、パフォーマンスに影響を与える2つのパラメーターがあります。初期容量と負荷率です。容量はハッシュテーブル内のバケット数であり、初期容量は単にハッシュテーブルが作成された時点の容量です。負荷率は、ハッシュテーブルの容量が自動的に増加するまでにハッシュテーブルが一杯になるのを許容する尺度です。ハッシュテーブル内のエントリ数が負荷率と現在の容量の積を超えると、ハッシュテーブルは、ハッシュテーブルのバケット数が約2倍になるように再ハッシュされます(つまり、内部データ構造が再構築されます)。
一般的な規則として、デフォルトの負荷率(.75)は時間とスペースのコストの間で良いトレードオフを提供します。値を大きくするとスペースのオーバーヘッドは減少しますが、検索コストは増加します(getやputを含む、HashMapクラスのほとんどの操作に反映されます)。初期容量の設定時には、マップ内の予想されるエントリー数とその負荷率を考慮して、再ハッシュ操作の数を最小限に抑える必要があります。初期容量が最大エントリ数を負荷率で割った値よりも大きい場合は、再ハッシュ処理は行われません。
すべてのパフォーマンス最適化と同様に、時期尚早に物事を最適化するのを避けることは良い考えです(すなわち、ボトルネックがどこにあるかについての硬いデータなしで)。
HashMap
のデフォルトの初期容量は16で、負荷率は0.75fです(つまり、現在のマップサイズの75%)。負荷率は、どのレベルでHashMap
の容量を2倍にするかを表します。
例容量と負荷率の積は16 * 0.75 = 12
となります。これは、12番目のキーと値のペアをHashMap
に格納した後、その容量が32になることを表しています。
実際、私の計算によると、「完全な」負荷率はlog 2(〜0.7)に近いです。これより小さい負荷率がよりよい性能をもたらすでしょうが。 .75はおそらく帽子から引っ張られたと思います。
証明:
バケットが空かどうかを予測することで連鎖を回避し、分岐予測を利用することができます。バケツが空になる確率が0.5を超える場合、バケツはおそらく空になります。
サイズをs、追加するキーの数をnとします。二項定理を使用すると、バケットが空になる確率は次のとおりです。
P(0) = C(n, 0) * (1/s)^0 * (1 - 1/s)^(n - 0)
したがって、バケツがより少ない場合、バケットはおそらく空です。
log(2)/log(s/(s - 1)) keys
Sが無限大になり、追加されたキーの数がP(0) = .5のようになると、n/sは急速にlog(2)に近づきます。
lim (log(2)/log(s/(s - 1)))/s as s -> infinity = log(2) ~ 0.693...
HashMapの容量を増やすために使い果たされる容量の量は?
負荷率はデフォルトでは初期容量の0.75(16)なので、容量が増加する前に25%のバケットが解放されます。これにより、新しいハッシュコードを持つ多くの新しいバケットが、増加した直後に存在するようになります。バケット数.
あなたが1.0を言うために負荷率を設定するならば、非常におもしろいことが起こるかもしれません。
HashCodeが888で、ハッシュコードを表すバケットが空いているハッシュマップにオブジェクトxを追加しているとします。したがって、object xがバケットに追加されます。 hashCodeも888である別のオブジェクトyを追加すると、あなたのオブジェクトyは確実にBUTの最後に追加されます(バケットはlinkedListの実装を格納するキー、value&nextだけなので] ==)これパフォーマンスに影響があります。検索を実行した場合、object yがバケットの先頭に表示されなくなっているため、所要時間はO(1)には依存しません。同じバケツにいくつのアイテムがあるか。ちなみにこれはハッシュ衝突と呼ばれていますが、これはあなたの負荷率が1未満のときでさえ起こります。
より低い負荷率 =より多くの空きバケット= 衝突のより少ない可能性 =高性能=高いスペース要件。
どこか間違っていれば訂正してください。
負荷率は、ハッシュテーブルの容量が自動的に増加するまでにハッシュテーブルが一杯になるのを許容する尺度です。
それは本当にあなたの特定の要件に依存します、初期の負荷係数を指定するための「経験則」はありません。
バケツがいっぱいになりすぎた場合は、それから私達は見なければなりません
非常に長いリンクリスト。
そしてそれはちょっとした意味での敗北です。
それで、これは私が4つのバケツを持っている例です。
私はこれまでのところ私のHashSetには象とアナグマがいます。
これはかなり良い状況ですね。
各要素には0個または1個の要素があります。
今度はHashSetにさらに2つの要素を追加します。
buckets elements
------- -------
0 elephant
1 otter
2 badger
3 cat
これもそれほど悪くはありません。
すべてのバケツには1つの要素しかありません。だから私が知りたいのなら、これはパンダが含まれていますか?
バケット番号1をすぐに確認できますが、そうではありません
そこと
私たちのコレクションにはないことがわかりました。
猫が入っているかどうか知りたいのなら、バケツを見る
3番
私は猫を見つけます、それが私たちの中にあるかどうか私はすぐにわかります
コレクション。
私がコアラを追加したらどうでしょう、それほど悪くはありません。
buckets elements
------- -------
0 elephant
1 otter -> koala
2 badger
3 cat
たぶん今バケットナンバー1の代わりに見るだけの代わりに
1つの要素
私は2つを見る必要があります。
しかし、少なくとも私は象、アナグマ、そして
ネコ。
私が再びパンダを探しているならば、それはバケツの中にだけあることができます
1と
カワウソ以外のものを見る必要はありません。
コアラ。
しかし今、私はバケツ番号1にワニを入れて、あなたはすることができます
多分これが起こっているところを見なさい。
バケツ番号1がどんどん大きくなっていくと
大きい、それから私は基本的にのすべてを見なければならない
見つけるべきそれらの要素
バケツ番号1にあるべき何か。
buckets elements
------- -------
0 elephant
1 otter -> koala ->alligator
2 badger
3 cat
他のバケットに文字列を追加し始めると、
そうです、問題はすべての人にとってますます大きくなっています
シングルバケツ.
バケツがいっぱいになり過ぎないようにするにはどうすればよいでしょうか。
ここでの解決策はそれです
"the HashSet can automatically
resize the number of buckets."
HashSetは、バケツが増えていることを認識しています。
いっぱいです。
この1つすべての検索の利点が失われています
要素.
そしてそれはもっと多くのバケツを作成するでしょう(一般に以前の2倍)そして
それから要素を正しいバケツに入れます。
だからここに別々のと私たちの基本的なHashSetの実装があります
連鎖しています。今度は "自己リサイズHashSet"を作成します。
このHashSetは、バケットが
いっぱいになりすぎて
もっとバケツが必要です。
loadFactorはHashSetクラスの別のフィールドです。
loadFactorは、1要素あたりの平均要素数を表します。
バケツ、
その上でサイズ変更したいです。
loadFactorは空間と時間のバランスです。
バケツがいっぱいになりすぎると、サイズが変更されます。
もちろん時間がかかりますが
バケツがあれば、時間を節約できます。
もう少し空。
例を見てみましょう。
これがHashSetです。これまでに4つの要素を追加しました。
象、犬、猫、魚。
buckets elements
------- -------
0
1 elephant
2 cat ->dog
3 fish
4
5
この時点で、私はloadFactor、
しきい値、
バケツあたりの要素の平均数は大丈夫です
と、0.75です。
バケットの数はbuckets.length、つまり6です。
この時点でHashSetには4つの要素があります。
現在のサイズは4です。
HashSetのサイズを変更します。つまり、バケットを追加します。
1バケットあたりの平均要素数が
loadFactor.
現在のサイズをbuckets.lengthで割った値が
loadFactorよりも大きい。
この時点で、バケットあたりの平均要素数
4を6で割った値です。
4つの要素、6つのバケツ、それは0.67です。
これは、0.75に設定したしきい値よりも小さいので、
はい。
サイズ変更する必要はありません。
しかし、今度はウッドチャックを追加するとしましょう。
buckets elements
------- -------
0
1 elephant
2 woodchuck-> cat ->dog
3 fish
4
5
ウッドチャックはバケツ番号3で終わるでしょう。
この時点で、currentSizeは5です。
そして今バケットあたりの要素の平均数
currentSizeをbuckets.lengthで除算した値です。
5つの要素を6つのバケットで割ったものは0.83です。
そしてこれは0.75だったloadFactorを超えています。
この問題を解決するために、
バケツおそらく少し
もっと空なので、
バケツが含まれています
要素はもう少し複雑になるでしょう、私はリサイズしたい
私のハッシュセット.
HashSetのサイズ変更は2つのステップを踏みます。
最初にバケツの数を2倍にします、私は6つのバケツを持っていました、
今、私は12バケツを持つつもりです。
ここで私が0.75に設定したloadFactorは同じままであることに注意してください。
しかし、変更されたバケットの数は12です。
同じ要素数は5です。
5を12で割ったものは約0.42です。
負荷率、
だから私たちは今大丈夫です。
しかし、これらの要素の一部が
今間違ったバケツ。
例えば、ゾウ。
ゾウの数が
象の文字
8でした。
6つのバケツがあります。8 - 6は2です。
それが2番になった理由です。
しかし今12個のバケツがあるので、8 mod 12は8です。
象はもうバケツ番号2に属していません。
象はバケツ番号8に属します。
ウッドチャックはどうですか?
ウッドチャックは、この問題全体を始めたものでした。
ウッドチャックはバケツ番号3で終わりました。
9 mod 6は3だからです。
しかし今、私たちは9 mod 12をします。
9 mod 12は9、ウッドチャックはバケツ番号9に行きます。
そして、あなたはこれらすべての利点を見ています。
バケット番号3には2つの要素しかありませんが、以前は3つでした。
これが私たちのコードです。
hashSetを別のチェーンで
サイズ変更はしませんでした。
さて、これがリサイズを使う新しい実装です。
このコードの大部分は同じです。
まだ含まれているかどうかを判断します。
すでに価値があります。
そうでなかったら、それから私達はそれがどのバケツであるかそれを理解するでしょう
に入る必要があります
それをそのバケットに追加し、それをそのLinkedListに追加します。
しかし今度はcurrentSizeフィールドを増やします。
currentSizeは数値を追跡するフィールドでした
私たちのHashSetの要素の。
私たちはそれを増やすつもりですそしてそれから見ていくつもりです
平均負荷では、
1バケットあたりの平均要素数.
ここでその分割を行います。
念のため、ここで少しキャストする必要があります。
ダブルになる.
そして、その平均負荷を現場と比較します。
私が設定したこと
たとえば、このHashSetを作成したときの0.75は、
loadFactor.
平均負荷がloadFactorより大きい場合、
つまり、1バケットあたりの要素数が多すぎるということです。
平均、そして私は再挿入する必要があります。
再挿入するメソッドの実装は次のとおりです。
すべての要素.
まず、oldBucketsというローカル変数を作成します。
これは、現在のバケツを指しています。
すべてのサイズを変更する前に.
注意:私はまだ新しい一連のリンクリストを作成していません。
バケツの名前をoldBucketsに変更しただけです。
今度はバケツが私達のクラスの分野だったことを覚えなさい、私は行く
新しい配列を作成する
リンクリストのリストですが、要素数は2倍になります。
それが初めてだったように。
今、私は実際に再挿入をする必要があります、
私はすべての古いバケツを繰り返します。
OldBucketsの各要素は文字列のLinkedListです。
それはバケツです。
私はそのバケツを通り抜けて、その中の各要素を取得します
バケツ。
そして今度はそれをnewBucketsに再挿入します。
そのハッシュコードを取得します。
私はそれがどのインデックスであるかを解明します。
そして今、私は新しいバケット、新しいLinkedListを取得します。
文字列と
その新しいバケツに追加します。
要約すると、HashSetsはLinkedの配列です。
リストまたはバケツ。
自己リサイズHashSetは何らかの比率を使って実現できます。
私はn * 1.5かn +(n >> 1)のテーブルサイズを選ぶでしょう、これは分割なしで.66666〜の負荷率を与えるでしょう。ハードウェア.
負荷率と再ハッシュの完全な理解は here
HashMapDEFAULT_INITIAL_CAPACITY = 16およびDEFAULT_LOAD_FACTOR = 0.75fの場合、MAX HashMapのALLエントリの数=16 * 0.75 = 12。 13番目の要素が追加されると、HashMapの容量(配列サイズ)は2倍になります。完璧なイラストがこの質問に答えました: 画像はここから取得されます。
https://javabypatel.blogspot.com/2015/10/what-is-load-factor-and-rehashing-in-hashmap.html