HashMap采用的是数组+链表+红黑树的形式。数组是可以扩容的,链表是可以转化为红黑树的。
用户可以设置的参数:初始总容量默认是16,默认的加载因子是0.75
容量*加载因子=阈值
什么时候扩容?
(1)当前容量超过阈值
(2)当链表中元素个数超过默认设定(8个),当数组的大小还未超过64的时候,此时进行数组的扩容,如果超过则将链表转化成红黑树。
什么时候链表转化为红黑树?
当数组大小已经超过64并且链表中的元素个数超过默认设定(8个)时,将链表转化为红黑树。
put过程
1.根据key计算出hash值
2.hash值&(数组长度-1)得到所在数组的index
3.如果该index位置的Node元素不存在,则直接创建一个新的Node
如果该index位置的Node元素是TreeNode类型即红黑树类型了,则直接按照红黑树的插入方式进行插入
如果该index位置的Node元素是非TreeNode类型则按照链表的形式进行插入操作,链表插入操作完成后,判断是否超过阈值TREEIFY_THRESHOLD(默认是8),超过则要么数组扩容,要么链表转为红黑树
4.判断当前总容量是否超过阈值,如果超出则执行扩容
扩容过程
按照2倍扩容的方式,那么就需要将之前的所有元素重新按照2倍桶的长度重新计算所在桶。这里为啥是2呢?
因为2倍的话,更加容易计算出它们所在的桶,并且各自不会互相干扰。如:原桶数组长度是4,现在桶数组长度是8,那么,原桶0中的元素会被分到新的桶0和桶4里面,原桶1中的元素会被分到新的桶1和5里面,原桶2中的元素会被分到新的桶2和桶6里面,原桶3中的元素会被分到新的桶3和桶7里面。为什么是这样呢?
桶0中的元素的hash值后2位必然是00,这些hash值可以根据后3位000或者100分成2类数据。它们分别&(8-1),即&111,则后3位为000的在桶0中,后3位为100的必然在桶4中。其他同理,也就是说,桶4和桶0重新瓜分了原来的桶0中的元素。
如果换成其他倍数,那么瓜分地就比较混乱。
这样在瓜分数据时,只需要先把这些数据分类,如上述桶0中分成000和100这两类,然后,直接构成新的链表,分类完成后,直接将新的链表挂在对应的桶下面即可。
get过程
1.根据key计算出hash值
2.hash值&(数组长度-1)得到所在数组的index
3.如果要找的key就是上述数组index位置的元素,直接返回该元素的值
如果该数组index位置的元素是TreeNode类型,则按照红黑树的查询方式进行查找
如果该数组index位置的元素是非TreeNode类型,则按照链表的方式进行遍历查找