[备份] Java HashMap 源码研究

介绍

Hash

Hash也称散列和哈希。简单理解是把任意长度的输入通过Hash算法变成固定长度的输出,原始数据映射后的二进制串就是哈希值。由于哈希值空间远小于输入空间,一定存在不同输入被映射到相同输出的情况,又被称为抽屉原理

特点:

Node

我们传入hashmap的item其实是node对象,首先来看node是怎样的,图里大致可以看出hashmap由一个node数组构成,在一些特殊情况下会转为红黑树,具体将在后续分析

img

Put

Node数组应该是2的次方倍长度,当我们put一个k-v时,会发生以下的事

其中路由算法大致是:(table.length-1)&node.hash

某种情况下,经过路由算法会得到相同的位置,正是这时候碰撞形成链表

当链表碰撞变地很长时候,链表查找效率会较低

源码分析

常量

默认长度16(1左移4位)和最大长度

负载因子0.75,后续分析

树化阈值(在什么情况下链表转为红黑树)

树降级阈值(在什么情况下红黑树转为链表)

另一个树化阈值,node数组长度达到64后才升级为树

属性

哈希表Node数组

暂时不用关心的属性

当前哈希表中元素个数

当前哈希表结构修改次数

当哈希表中元素超过阈值时扩容

负载因子,一般不会改,作用是计算threshold threshold = capacity * loadFactor

构造方法

双参数构造方法

跟入tableSizeFor 返回一个大于等于cap的数,并一定是2的次方 该函数比较巧妙,可以自行带入一个数进行计算,效果是恰好得到当前数下一个2的次方数 例如输入9会得到16,输入15也会得到16。将输入二进制每一位置1并在最后加1进位

另外3个套娃构造方法,都调用了双参构造方法

putVal

hashmap.put本质是putVal方法

扰动函数

跟入hash方法,这里对key进行了扰动,得到是正是node中的hash属性

右移16位让key的哈希值高16位和它本身进行异或运算(相同得0不同得1)

16位正好为32位的一半,自己的高半区和低半区做异或,为了混合原始哈希吗的高位和低位,来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,使高位的信息也被保留下来

总之:让哈希值更加散列,减少寻址时的哈希冲突

参考大佬表格 img

putVal方法

注意到传入两个bool

具体分析

resize

如果node中元素链表或树包含node过多,也就是链化严重,这时候查询效率会过低

resize方法分两步:

计算新容量和新阈值分析

实现扩容分析(参考图片) img

get

get方法本质是getNode方法

getNode分析

remove

remove方法

跟入removeNode

replace

两个方法比较简单