在这里说的是jdk1.8中hashmap实现原理,在1.8中采用的数组+链表+红黑树,每个数组都是一个链表,当链表长度大于6时转为红黑树存储,如果红黑树长度小于6时又变为链表存储。
在看源码注释时看到作者说数组长度必须为2的幂次方,所谓的幂次方就是乘以2的次数,2^3就是16,在源码中有段 tab[i = (n - 1) & hash] 参数n表示的数组长度,作者将数组长度减1然后让其与key的hashcode值进行换位与运算,16-1 =15,15的二进制是1111,hashcode值就是一串int数字,假设key的hashcode值为20978673,转为2进制后为1 0100 0000 0001 1011 1111 0001,换位与运算进行的是低位,15的二进制是4位,位数不够以0补齐位数与hashcode进行低位的比较,换位与运算法则是位数上的值等于1就取1,否则取0,以上两个数进行比较后得到0001,该值就是数据要存储的下标值,转为十进制就是1,该值是数组长度(16-1)(0-15)中的一个位置,作者之所以说明长度必须为2的幂次方,是因为在将key的hashcode值与数组长度进行换位与的运算中为了匹配更多的1结果。
如果不按作者说的来自定义数组长度,定义的长度无非就是奇数或者偶数,将二者转为二进制后查看可知奇数值中1的数量更多,偶数中0的数量更多,由此可知作者的用意了,因为运算符是&,位数上的值都为1就取1,否则取0,作者要求长度为2的幂次方就是为了让当前数组长度经过二进制转换后得到更多的1,如果自定义的长度不是2的幂次方是任意一个数,比如是18,让其减1后为17的奇数,转二进制后是1 0001,如果按这个值进行运算最终得到的值只能是有两位相等取1其他位都为0,转换为十进制后的下标值只可能是1或者17,如果按这个存储数据那真的是要爆炸了,因为数组中其他的下标位置根本用不上,并且不断加大链表、红黑树的长度,影响查询效率。
如果是2的幂次方,如8、16、32、64、128等,你将其减1操作后转为二进制会看到值都是1,这样让其参与换位与运算就扩大了与hashcode值的碰撞几率,使下标值的生成更加分散,充分发挥数据存储效率与查询效率。