hashMap tableSizeFor 实现原理
Posted yangmengdx3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hashMap tableSizeFor 实现原理相关的知识,希望对你有一定的参考价值。
基于jdk1.8
hashMap实现,要求容量大小是2的整次方,例如:2/4/8/16/32/64/128...,而不能是中间的某个值。这是为什么呢?
map是数组+链表的数据结构,读写数据都需要首先获取数组中的下标值,获取的方式是通过hashcode取余。取余so easy,我们都会,假定运算后的hashcode=17,容量大小capacity=16,17%16=1,很容易得出元素落在数组的下标[1]内。
但是还有一种方式可以获取到正确的下标值,17 &(16-1)=1。
17二进制: 10001
15二进制: 01111
17&15: 00001 = 1
计算机的物理特性,决定位运算才是取余的正确打开方式。但是却有一个限制,被位与的数值有效位必须全部都是1,如15:1111可以,13:1101则不行。
这里位与运算得到的是下标位置,数组容量要在最大下标值的基础上加1,等价于二进制中被位与的数值进位,如15进1位=16:10000,2的4次方。
如果我们new HashMap<>(13),输入一个非整次方的数值,hashmap会自动调整成最近的整次方,例如这里的13最终会转换成16,实现方法为:java.util.HashMap#tableSizeFor
怎么实现呢?
2的整次方的特性是二进制有效位只有一个1,退位后当前1消失,后面bit位全补1,例如16:10000,退位后01111。13的二进制:1101,结构上看只要第二个bit补1(1101->1111),再进位(1111->10000)就可以了。1101->1111->
我们来分析实现
首先把方法复制出来,加上一些日志方便分析:
static final int MAXIMUM_CAPACITY = 1 << 30; static final int tableSizeFor(int cap) { System.out.println(Integer.toBinaryString(cap)); int n = cap - 1; System.out.println(Integer.toBinaryString(n)); n |= n >>> 1; System.out.println(Integer.toBinaryString(n)); n |= n >>> 2; System.out.println(Integer.toBinaryString(n)); n |= n >>> 4; System.out.println(Integer.toBinaryString(n)); n |= n >>> 8; System.out.println(Integer.toBinaryString(n)); n |= n >>> 16; System.out.println(Integer.toBinaryString(n)); return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } public static void main(String[] args) { tableSizeFor(MAXIMUM_CAPACITY); }
int n = cap - 1先退位,所以最终实现的结果是n的bit位全补1。
使用的最大容量+1,这样看日志比较清晰。
1000000000000000000000000000001 1000000000000000000000000000000 1100000000000000000000000000000 1111000000000000000000000000000 1111111100000000000000000000000 1111111111111111000000000000000 1111111111111111111111111111111
看到规律了吗?tableSizeFor首先获取最高位的1,二进制退位规则决定一定能够获取到最高位的1,然后进行不停的bit复制,1生2,2生4等等。int类型只有32位,所以复制到16位终止。
最后将n进位,即得到2的整次方,不过限定不能大于1>>30;
以上是关于hashMap tableSizeFor 实现原理的主要内容,如果未能解决你的问题,请参考以下文章
n |= n >>> 1——JDK10的HashMap原理 tableSizeFor(initialCapacity)方法
HashMap中一个精巧算法 tableSizeFor(int cap)