为什么HashMap的长度一定是2的次幂呢?
今天和朋友聊天被问到HashMap的数组长度为什么是2的倍数。说实话挺惭愧的,秋招结束了,还不能完整的给出一个完整的答案。
今天和朋友聊天被问到HashMap的数组长度为什么是2的倍数。说实话挺惭愧的,秋招结束了,还不能完整的给出一个完整的答案。
我知道了HashMap的数据结构,也知道了什么是Hash冲突,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
对于第一个问题针对的是hashmap数组扩容时,新数组length = 原数组length * 2,沿用前面的例子(array.length = 2^4 = 16,二进制10000),array.length 乘以 2 ,即二进制左移一位,由 10000 变成 100000。此时需要重新计算数组槽中的元素位置,如果槽中是链表,链表中每个元素都需要重新计算位置(这里不考虑红黑树)。t同时,由于get时需要对链表其进行遍历,链表越长检索效率越差。那么,计算出的key值落点越平均,hash冲突的可能性越小。key值的落脚点为key的hash值与数组长度作取余操作,记作key.hashcode % array.length。
数学角度考虑,保持array.length为质数会使得计算结果更均衡,hashTable就是这么做的(数组初始值11)。但 hashmap 中 array.length 偏偏选择了2的次幂,是个合数.完全出于性能考虑!
结论:当 array.length长度是2的次幂时,key.hashcode % array.length等于key.hashcode & (array.length - 1)。
以长度为16 , key值为 10011011001 举例,也就是
array.length = 16 , 二进制为:10000
array.length - 1 = 15 , 二进制为 : 1111
key.hashcode % array.length : 1001
key.hashcode & array.length-1 : 1001
计算过程:
100 1101 1001
& 1111
1001
发现两者计算的结果是一样的。
最终计算的结论就是:
10011011001 & ( 10000 - 1 ) = 10011011001 & 1111 = 1001 = 10011011001 % 10000
总结:
对hashmap而言,数组长度为2次幂有两点好处:
& 代替 %,可以提升性能
数组扩容时,仅仅关注 “特殊位” 就可以重新定位元素
仅关注 “特殊位” 就可以重新定位元素
性能,性能,还是性能……