為了實現一個通過 Hash 尋址的方式,獲得存儲的元素,首先要考慮的選擇何種 Hash 算法。 HashMap 選擇了近似於取模的方式,獲得元素存儲的位置。當然為了提高性能,且降低key 的碰撞。HashMap主要做了兩方面的優化
1. 對Hash取值的優化
我們當然可以直接選擇存儲的 key 的 hashcode() 方法得到 key 的 hash 值。但是對一些hashcode值比較接近的key,可能會造成存儲時,分配不均。通常情況下,決定key 分配位置的其實是 key 的hash 值的低位部分。所以,為了
讓 hash 值在分配時更加均勻,可以采用如下方法
static final int spread(int h) { return (h ^ (h >>> 16)) & 0x7fffffff; }
2. 對數組大小的優化
一般情況下,我們可以直接采用 hash % CAP (CAP為具體存儲數組的大小) 得到某個 key 在數組的下標。但這種取模的方式相對性能較低,HashMap 采用了另外一種方式,即將數組的容量設定為 2 的 n 次冪,再使用 與 的形式獲得下標。
這里面主要涉及到了如下算法
/** * Returns a power of two table size for the given desired capacity. * See Hackers Delight, sec 3.2 */ private static final int tableSizeFor(int c) { int n = c - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
進行下標的計算時,我們可以使用如下方式得到:
(spread(key.hashcode() & (table.length-1))
上述 tableSizeFor 方法的目的是找到比給定 c 最近的 2 的 n 次冪的最小值
至於 tableSizeFor 方法的原理,大家可以參考一下這篇文章
參考資料: