私は次のことを見て、JavaのHashMapソースコードを調べていました。
//The default initial capacity - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 16;
私の質問は、なぜ最初にこの要件が存在するのですか?また、カスタム容量でHashMapを作成できるコンストラクターは、2の累乗に変換します。
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
なぜ容量は常に2の累乗でなければならないのですか?
また、自動リハッシュが実行されると、正確にはどうなりますか?ハッシュ関数も変更されていますか?
マップは、任意のint
値(負の場合もある)を[0, table.length)
。いつ table.length
は2のべき乗であり、実行できますreally安く、そしてindexFor
で:
static int indexFor(int h, int length) {
return h & (length-1);
}
テーブルの長さが異なる場合、余りを計算し、それが負でないことを確認する必要があります。これは間違いなくマイクロ最適化ですが、おそらく有効なものです:)
また、自動リハッシュが実行されると、正確にはどうなりますか?ハッシュ関数も変更されていますか?
どういう意味かよくわからない。同じハッシュコードが使用されます(各キーでhashCode
を呼び出すことによって計算されるため)。ただし、テーブルの長さが変更されるため、テーブル内での分布は異なります。たとえば、テーブルの長さが16の場合、ハッシュコード5と21の両方がテーブルエントリ5に格納されます。テーブルの長さが32に増えると、それらは異なるエントリになります。
理想的な状況は、実際にHashMap
のバッキング配列に素数サイズを使用することです。そうすることで、キーがアレイ全体に自然に分散されます。ただし、これはmod分割で機能し、その操作はJavaのすべてのリリースで次第に遅くなりました。ある意味では、2のべき乗のアプローチは、ハッシュコードの実装が適切でないと、配列にキーの衝突が発生する可能性が高くなるため、想像できる最悪のテーブルサイズです。
そのため、JavaのHashMap
実装には、貧弱なハッシュコードを補正するhash(int)
である別の非常に重要なメソッドがあります。