HashMap 的插入过程
- 若哈希表没有初始化,先进行初始化
- 根据key 的值计算出该键值对在数组中的下标位置i
- 判断table[i]是否为空,若为空,直接插入;
- 若不为空,则判断当前的key 与 table[i] 保存的key 是否相同,若相同则直接覆盖;
- 若不同,先判断当前的table[i]是不是红黑树,若是,就按照红黑树的插入算法进行插入
- 若是链表结构,则遍历链表,使用equals() 方法判断key是否存在,若存在则直接覆盖。否则插入链表末尾
代码示例
HashMap<String,Integer> map = new HashMap<>();
map.put(null,10);
map.put("dd",20);
1、执行new HashMap<>()
之后,哈希表只做一件事情:将哈希表的装载因子赋值为默认的0.75
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // 初始化默认的加载因子
}
可以发现,此时并没有实际申请哈希表的内存,和 ArrayList
一样;
2、执行map.put(null,10);
第一次执行put时,首先会判断底层的数组是否被初始化,如果没有初始化,则先进性初始化。
若已经执行过初始化,则开始对 key 进行处理
if ((tab = table) == null || (n = tab.length) == 0) // table 就是哈希表底层的数组
n = (tab = resize()).length;
3、处理 key
计算出 key = null 在数组内的下标
i = (n - 1) & hash = (16-1) & hash(null) = 0
得到该key在数组中的下标之后,首先判断 table[i] 是否为空,若为空则将(null,10)封装成一个 Node,放入 table[i] 中.
若table[i] 不为空,则会进行以下判断:
- 判断两个key 是否存在,若已经存在相同的key, 则直接覆盖。若不存在,进行下一步
- 判断是否为 treeNode,若是的话,直接将键值对插入红黑树
- 若不是 treeNode,开始遍历链表进行插入,若 key 已经存在,则直接覆盖;若不存在。将此键值对插入俩表末尾;若链表长度大于8,先转化为红黑树再插入;
第三句执行 map.put("dd",20);
时,首先也会判断table数组是否被初始化,显然第二步已经初始化了,所以直接对 key=dd 进行处理,计算得数组下标的位置为0**(null 和 dd 计算出的下标位置相同)**
4、最后更新哈希表中键值对的个数size
; 若新的个数大于 threshold (threshold=capacity*loadFactor)
,则调用resize()
函数进行扩容。