いい加減ハッシュテーブルは実装レベルで理解しくべきということで,ざっと読んだときの気付きをメモしておく。10年前は読めなかったけど,今回はさくっと読めた。(読んだのは,Adopt Open JDK 13)
テーブル内部では以下のクラスでデータが管理されていた。要するにClosed Addressingで実装されている。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
/** 中着 */
}
余談だが,Eclipse Collection(10.1)のUnifiedMapもOpen Addressingだったが,こちらはポインタでチェインせずに配列でチェインしていた。Open Addressingのようにメモリのキャッシュを効かせたいことが理由の様子。
ハッシュ値の計算。
要素数が少ないときには,下位ビットだけで格納場所が決まってしまうことを避けるため,上位ビットも使えるように下位16bitに上位16bitのXORをとっていた。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
ハッシュテーブルの引き方。
テーブルの要素数nから1引いたものとAND演算した値をindexとしていた。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
ちなみにテーブルの要素数(上記のtab.length)数は,2の倍数になっているようで,そこから-1すると全てのビットが立つので,下位ビットを残す演算になっている様子。