为了实现一个通过 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 方法的原理,大家可以参考一下这篇文章
参考资料:
Patience is always a virtue that worth your effort.
The usefulness of a cup is its emptiness.
浙公网安备 33010602011771号