问题:为什么ConcurrentHashMap每次扩容的时候扩大一倍?
答案:通过位运算,代替模运算,速度更快
众所周知:key要通过哈希函数变成一个数字,然后存放在哈希表对应的位置中。存放会涉及到一些哈希冲突的时候,就通过链表,或者平衡二叉树,将新key放在旧key后面。
例子:"name"(key) "zhangsan"(value) ,当前的哈希表容量是8
1. 通过java的hashCode() 函数可以计算出哈希码
2. “name”字符串的哈希码是3373707(十进制),1100110111101010001011(二进制)
3. 将1100110111101010001011与哈希表的容量8-1 = 7做一次与运算(&)
前面的位数忽略只保留最后三位结果显示是011也就是十进制的3
4. 将key放入大小为8的哈希表中
此处省略value5. 其他的key:
“age” 96511 10111100011111111 最后三位是111 与 7(111)做与运算得到111,放入第七个空格中
“email” 96619420 101110000100100101110011100,做与运算,放入100位置,也就是4号空格中
"phone" 194811 101111100011110011,与7做与运算,放入3号空格,哈希冲突,以链表形式放到name后面
这个时候哈希表已经变成这样子啦6. 当我们要扩展哈希表为16个空格的时候,16-1=15 ,15的二进制是1111
这个时候我们再比较一下
“name”的哈希码1100110111101010001011
“phone”的哈希码101111100011110011
当他们与15(1111)做与运算的时候,和与 7(111)做与运算的时候有什么不同?
“name”:
name“phone”
phone这时候,name的结果是(11)1011,“phone”结果是(3)0011
发现:name因为高一位是1,所以应该离开到11号空格,phone因为高一位是0,还会留在3号空格中!!
这就是扩大一倍带来计算上的简便!!
当初容量是8,8-1=7,7的二进制是111
现在容量是16,16-1=15,15的二进制是1111
因为他们都是全为1的形态,而扩大一倍后只是高位加了一个1,所以会留有大概一半的key在原来的空格,而有一半的key到相同的别的空格中,比如都是从3号位置到了11号位置中,这样也便于复制。
通过位运算,代替了模运算!
网友评论