HashMap的数组长度一定是2的次幂

博客原文:https://mp.weixin.qq.com/s

面试官Q1:为什么HashMap的长度一定是2的次幂呢?

如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。那么HashMap底层到底做了什么,使得Hash值散列、均匀分布呢?

以JDK1.8为例,查看下面一段代码:

1    public V put(K key, V value) {
2    return putVal(hash(key), key, value, false, true);
3    }

然后再点进putVal 方法,则会看到有下面的代码:

1        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
2                   boolean evict) {
3        Node<K,V>[] tab; Node<K,V> p; int n, i;
4        if ((tab = table) == null || (n = tab.length) == 0)
5            n = (tab = resize()).length;
6        if ((p = tab[i = (n - 1) & hash]) == null)
7            tab[i] = newNode(hash, key, value, null);

我们需要关注地方是这么一段代码:

1  tab[i = (n - 1) & hash]

这里是计算数组索引下标的位置
&为二进制中的与运算,它的运算特点是,两个数进行&,如果相同则为1,不同则为0。

因为hashMap 的数组长度都是2的n次幂 ,那么对于这个数再减去1,转换成二进制的话,就肯定是最高位为0,其他位全是1 的数。

那以数组长度为8为例(默认HashMap初始数组长度是16),那8-1 转成二进制的话,就是0111 。 那我们举一个随便的hashCode值,与0111进行与运算看看结果如何:

数字8减去1转换成二进制是0111,即下边的情况:

 1 第一个key:      hashcode值:10101001    
 20111进行&运算        &      0111                                      
 3                                0001  (十进制为14   ------------------------------------------                           
 5 第二个key:      hashcode值:11101000    
 60111进行&运算        &       0111      
 7                                 0000  (十进制为08--------------------------------------------               
 9 第三个key:      hashcode值:11101110    
100111进行&运算        &       0111      
11                                 0110  (十进制为6

这样得到的数,就会完整的得到原hashcode 值的低位值,不会受到与运算对数据的变化影响。

数字7减去1转换成二进制是0110,即下边的情况:

 1 第一个key:      hashcode值:10101001    
 20111进行&运算        &       0110                                      
 3                                 0000  (十进制为04   ------------------------------------------                           
 5 第二个key:      hashcode值:11101000    
 60111进行&运算        &       0110      
 7                                 0000  (十进制为08--------------------------------------------               
 9 第三个key:      hashcode值:11101110    
100111进行&运算        &       0111      
11                                 0110  (十进制为6

通过上边可以看到,当数组长度不为2的n次幂 的时候,hashCode 值与数组长度减一做与运算 的时候,会出现重复的数据,因为不为2的n次幂 的话,对应的二进制数肯定有一位为0 ,这样,不管你的hashCode 值对应的该位,是0还是1 ,最终得到的该位上的数肯定是0,这带来的问题就是HashMap上的数组元素分布不均匀,而数组上的某些位置,永远也用不到。如下图所示:
在这里插入图片描述
这将带来的问题就是你的HashMap 数组的利用率太低,并且链表可能因为上边的(n - 1) & hash 运算结果碰撞率过高,导致链表太深。(当然jdk 1.8已经在链表数据超过8个以后转换成了红黑树的操作,但那样也很容易造成它们之间的转换时机的提前到来),所以说HashMap的长度一定是2的次幂,否则会出现性能问题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值