HashMap源码解析
之前没看过JKDK源码,这次课老师给讲了集合,顺便用HashMap的源码将我击溃。今天花时间看一下HashMap,奋起反击。
一、HashMap基本概念
基本属性
threshold: resize的阈值, 正常等于capacity * loadFactor; 当item数量大于threshold时,
loadFactor: 加载因子,默认0.75
Node组成
二、伪代码
new HashMap时, 初始化loadFactor;
put(k, v)时,如果是首次put,table为空,先resize(),构建一个新的数组;
put时,做hash处理,找到hash槽,放进去;如果这个槽筒大于树化阈值,就进行树化处理;
put后,如果size > threshold, 就resize, 重新hash。
二、HashMap核心代码分析
1. 实例初始化
HashMap初始化,会使用初始容量和加载因子来初始化。
new HashMap() 初始化结果:threshold=0, loadFactor=0.75, 使用DEFAULT_INITIAL_CAPACITY=16做初始容量
new HashMap(initCapacity) 初始化结果: threshold=大于等于initCapacity的最小2的幂。
new HashMap(initCapacity, loadFactor)
初始化完成后,并不创建容器空间,而是在首次put时创建。
2、容器初始化
首次put时,由于table为null,先走了一次resize()
首次resize时,oldCap = 0, oldThr = 初始threshold; newCap = 初始threshold, newThr = 初始threshold * 加载因子。根据newCap,初始化数组table。
3、put时找hash槽
关键算法(容量必须是2的幂,使用位运算,能够秒速找到):
(n - 1) & hash
4、HashMap扩容
扩容为原容量的2倍,原容量的那些位置被称为low位,新扩出来的被称为high位。重新hash时,要么还在原位置待着,要么被hash到对应的高位去。
难点解析:如果有当前桶有多个item,则分成两组,一组重hash到low位,一组重hash到高位。
5、树化
树化条件, 根据条件不太容易出现树化的场景。
binCount >= TREEIFY_THRESHOLD - 1 不满足,就不去尝试树化
tab.length >= MIN_TREEIFY_CAPACITY(64) 树化时,不满足只会resize
树化只是树化一个桶的存储。整个table的存储结构并不动。