文章目录

  • 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 确定桶下标为什么采用位运算而不是取模运算?

  1. 从运算时间上来讲,位运算的效率远远好于取模运算

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的幂

简而言之,还是基于效率上的考虑。即 位运算的运行效率要好于数学运算;

具体体现在:

  1. 数学运算从位运算实现。
  2. 参考2.4

这篇文章主要从HashMap的设计思路上来说明其设计之巧妙…