JDK 1.7
jdk 1.7的HashMap 使用 数组 + 链表的形式保存数据
- 数组数据结构是 Node<K,V>[] 和链表的是一样的(图中Node类)
- 数组初始长度为 16 扩展因子0.75
- 当数组达到 16 * 0.75 = 12时对数组扩容,每次扩展 (原来长度) << 1,就是原来两倍。
<< n 符号就是左移n位
例如 1 << 1 = [1的2进制,左移1位,右边补0] = 10 = 2 也就是 1 * 2^1 = 2
n << x = n * 2^x
get 流程
get 和 put 都必须走的一步就是计算数组下标,找到对应下标再从链表中获取数据。
-
取key的hashCode结果是一个整数类型,对应32位的二进制地址
例如:结果:514287189521 对应地址为:1111 1111 1111 1111 0000 0000 0001 0001
-
对hashCode 进行优化,代码中体现为 hashcode = hashcode ^ ( hashCode >>> 16)
为什么要做这一步?
首先我们需要知道从hashCode如何计算出数组下标。
举个简单例子:
现有数组 Node[4] 长度为4
当 hashCode = 99 = 1100011【二进制】
99 % 4 = 3 = 11【二进制】
当 hashCode = 257= 100000001【二进制】
257% 4 = 1= 1【二进制】
我们可以发现都是二进制后面几位(取决于数组长度),也就是取模 hashCode的高位是基本不参与的。除非数组长度很长(很少出现),因此hash冲突比较多,因为参与计算的数少。
所以想办法让高位参与计算,即可减少hash冲突
回到 hashcode = hashcode ^ ( hashCode >>> 16) 是如何让高位参与计算的问题上。
因为:8589869073 >>> 16 = 1111 1111 1111 1111 0000 0000 0001 0001 >>> 16 = 0000 0000 0000 0000 1111 1111 1111 1111
所以:8589869073 ^ ( 8589869073 >>> 16 ) = 1111 1111 1111 1111 0000 0000 0001 0001 ^ 0000 0000 0000 0000 1111 1111 1111 1111 = 1111 1111 1111 1111 1111 1111 1110 1110
此时再取模高位也参与进来了 -
使用新的hashCode 和 数组长度取模,得到下标
-
根据下标获取对应数组元素的链表
-
根据key遍历链表取值
put 流程
和get一样找到对应链表往后面加上元素