基于jdk1.8java
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。spa
17二进制: 10001日志
15二进制: 01111code
17&15: 00001 = 1blog
计算机的物理特性,决定位运算才是取余的正确打开方式。可是却有一个限制,被位与的数值有效位必须所有都是1,如15:1111能够,13:1101则不行。ci
这里位与运算获得的是下标位置,数组容量要在最大下标值的基础上加1,等价于二进制中被位与的数值进位,如15进1位=16:10000,2的4次方。hash
若是咱们new HashMap<>(13),输入一个非整次方的数值,hashmap会自动调整成最近的整次方,例如这里的13最终会转换成16,实现方法为:java.util.HashMap#tableSizeForit
怎么实现呢?
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;