4、HashMap
4.1 先看看属性4.2 构造方法4.3 从put分析扩容机制(重点)
putVal方法resize方法(扩容和树化)
扩容树化
扩容机制总结
4.4 get方法4.5 remove方法4.6 replace
往期:
Collection集合工具类源码解读(一) — ArrayList 和 VectorCollection集合工具类源码解读(二) — linkedList 4、HashMap
重头戏来咯,老惯例,先写个demo,debug
public class HashMapDemo {
public static void main(String[] args) {
HashMap
4.1 先看看属性
DEFAULT_INITIAL_CAPACITY:默认初始容量为16
注意:刚new出来是0,往里添加数据后第一次resize()才到16,等下分析 MAXIMUM_CAPACITY:最大的容量:2的30次方DEFAULT_LOAD_FACTOR :默认加载因子loadfactor:0.75
用来计算扩容门槛,比如现在容量为16,16*0.75=12,即table满了12个容量就扩容,一次扩两倍 TREEIFY_THRESHOLD:默认树化门槛:8UNTREEIFY_THRESHOLD:默认从树转换回链表的门槛:6MIN_TREEIFY_CAPACITY:最小树化容量:64
注意:①jdk1.8之后hashmap才是数组+链表+红黑树,jdk1.7和之前都是数组+链表,没有红黑树②必须满足table容量到64,并且其中table数组中的一个格子链接了长度为8的链表,这时候该链表才会树化,转换为红黑树 4.2 构造方法
一共四个:
public HashMap():默认无参,loadfactor = 0.75public HashMap(int initialCapacity) :设置初始容量,loadfactor = 0.75public HashMap(int initialCapacity, float loadFactor):设置初始容量和loadfactor
注意:loadfactor需要在0~1范围内,否则永远不会扩容 (后续分析) public HashMap(Map extends K, ? extends V> m):往hashmap中插入另一个map 4.3 从put分析扩容机制(重点)
- put进去后先算key的hash值,如果key为空,hash为0,否则就用key的hashcode异或key右移16位然后是进入putVal方法: 这个方法很重要,需要细说一下
- tab在判断时赋值,把原先的table赋给了tab,第一次进入putVal时,table为空,走进第一个if( 第一次put必然 )
进行 第一次resize 其他情况就是table不为空
- 如果该hash对应的table[i]位置为空:就直接放进去如果该位置已经有元素了:
- 判断第一个元素的hash以及key和想要put进来的元素的hash以及key 是否相等,相等就让e = 这个相等的元素判断这个位置连接的是否是红黑树:是就走红黑树的方法putTreeval否则就循环遍历这条链表,找是否有hash以及key 相等的元素,
- 有相等的就让e = 这个相等的元素没有相等的就找到这条链表的尾部,追加到尾部
- 进来之后先拿到原来的大小oldCap,原来的扩容门槛oldThr,大括号是第一个if语段
- 原来的容量不为空:在oldCap不超过最大容量MAXIMUM_CAPACITY的情况下,将容量和门槛左移一位:意思是扩容直接翻2倍原来的容量为空,但是门槛不为空:新的容量就是原来门槛的大小原来容量为空,门槛也为空:(第一次resize):设置容量为16,门槛为16*0.75=12
- 在上一个if语段里,如果进入了oldCap>0,且oldThr=0的情况下,newThr翻2倍任然是0,所以在这里需要再次判断将newThr设置为newCap*loadFactor
树化
再来讲一下扩容什么时候变成树的
如图是一个demo,默认构造方法hashmap的扩容门槛threhold是12,Person类的hashcode都是一致的,所以能插在同一索引位置上
所以:执行完断点前面的程序后,size已经满足了12,且Person那条链表长度以及到达了8,如图
在put第13个值时,打断点进去,发现其执行了treeifyBin(tab,hash),是因为他的链表长度超过了树化的长度TREEIFY_THRESHOLD = 8
最终执行完之后可以看到,容量扩到了32,门槛到了24
同样的,put14的时候,容量会扩到64,门槛到48,过程不截图了
在put 15 的时候,注意,在此之前已经满足①链表长度>=8 ②table表容量64 :意思是要开始树化了
同样的,进入treeifyBin(tab,hash),但是这次走else if,进行红黑树化,如图:
treeify()就是对原来的树进行红黑树的格式化了,就不走进去了
最后发现,原来的Node已经被重构成了TreeNode
扩容机制总结 4.4 get方法
get:
调用getNode,如果查到有值就返回该值,查不到就返回null getOrDefault:
调用getNode,如果查到有值就返回该值,查不到就返回设置的默认值 4.5 remove方法
remove(Object key):删除一个KV。调用removeNode方法,分析写在图上了,显而易见 4.6 replace
replace(key,oldValue,newValue):找到key,oldvalue对应的结点替换这个值replace(key,value):找到key对应的结点替换这个值



