文章目录
- 1. HashMap的灵魂
- 1.1 hash()
- 1.1.1 这样设计的目的?
- 2. HashMap的精髓
- 2.1 key 为null的hash值?
- 2.2 如何确定桶下标?
- 2.3 确定桶下标为什么采用位运算而不是取模运算?
- 2.4 位运算如何保证下标不越界呢?
- 3. HashMap容量必须为2的幂
1. HashMap的灵魂
HashMap的核心操作都是依靠着hash()展开的,在去看HashMap的操作之前,首先先了解一下hash()。
1.1 hash()
key的哈希值就是自身的hashCode的高16位和低16位进行异或运算得到的。
static final int hash(Object key) {
int h;
// h >>> 16 右移16位,高位补0,取出高16位
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
1.1.1 这样设计的目的?
哈希表的容量被设计为2的N次方,则当某个key的hashCode()值 > table.length,则高位就不会参与到hash的计算。通过hashCode的高16位异或低16位,这样让高位也能参与到hash的计算当中,从而降低hash冲突的风险。
2. HashMap的精髓
下面问题的重要性不言而喻…
2.1 key 为null的hash值?
通过hash()方法,我们看到当key == null 时,其hash值为0。从而保证了key为null只能有一个。
2.2 如何确定桶下标?
这里需要我们注意的是:
HashMap中的桶下标是通过 (n-1)& hash 来计算的,而不是取模运算(hash%n)来计算。
2.3 确定桶下标为什么采用位运算而不是取模运算?
- 从运算时间上来讲,位运算的效率远远好于取模运算
2.4 位运算如何保证下标不越界呢?
HashMap在设计上其容量必须是2的n次幂,用心良苦啊。
当 n 是 2的次幂时, n -1 的二进制表示法的尾部都是以连续1的形式来表示的。这样当(n-1)与hash进行 与运算 时,会保留hash中后x 位的1,这样就保证了索引值不会超过数组长度。
当 n 为 2的方时,满足:(n-1) & hash = hash % n
3. HashMap容量必须为2的幂
简而言之,还是基于效率上的考虑。即 位运算的运行效率要好于数学运算;
具体体现在:
- 数学运算从位运算实现。
- 参考2.4
这篇文章主要从HashMap的设计思路上来说明其设计之巧妙…