一马平川
不积跬步无以至千里,后继才能薄发

HashMap源码学习笔记

2020年09月16日
0
未分类
未设置标签

HashMap源码学习笔记

HashMap核心属性解析

DEFAULT_INITIAL_CAPACITY(默认初始容量)

默认初始容量是在构造HashMap时,不定义初始容量默认指定的大小,默认大小为1<<4也就是16;

MAXIMUM_CAPACITY(最大容量)

最大容量是单个HashMap实例所能装载的最大容量,值为1<<30,也就是536,870,912个元素;

DEFAULT_LOAD_FACTOR(默认装载因子)

默认装载因子,在构造HashMap时,不指定装载因子时,默认为0.75f。装载因子的作用是留一部分的容量作为冗余,避免hash之后过多元素分配到一个桶里,导致hashmap退化成链表。

MIN_TREEIFY_CAPACITY(最小树化容量阈值)

最小树化阈值是jdk1.8中加入的,控制HashMap在什么情况下会将桶中的链表转为红黑树,默认为64

TREEIFY_THRESHOLD(树化阈值)

树化阈值是在jdk1.8中被加入的,默认为8,应用在我们常听说的红黑树中,在HashMap实例的元素数量大于最小化树化阈值且大于树化阈值时,就会将链表转为红黑树。

UNTREEIFY_THRESHOLD(链表化阈值)

链表化阈值是jdk1.8中被加入,默认为6,之所以不与树化阈值相等,是为了在同一桶中的元素增删时不会导致频繁树化和链表化。

HashMap核心方法解析

hash()

这是hashmap核心的方法之一,也是HashMap中的“Hash”的体现,其中进行了一次hash计算和对hash值进行和高16位进行异或计算,也就是其中的 (h = key.hashCode()) ^ (h >>> 16) ,hash方法之所以要这样“多此一举”,目的是为了在使用hash值进行映射时避免与运算丢失高位特征,以保障元素趋于平均分布。

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

putVal()

这是hashmap的put()方法中最终调用的方法,也是添加元素功能的实现方法;其中hashmap采用了lazy-load的思想,构造HashMap实例时并不会对内部数组进行初始化,而是在首次put的时候才进行。
其中逻辑:

  1. 判断是否需要初始化,若需要初始化则调用resize() 方法,并返回初始化的容量大小。
  2. 进行hash()计算,通过与运算获取元素实际插入的下标,判断下标中的桶空间是否有节点,若没有节点则新建node节点,进入5,否则进入3流程。
  3. 获取对应桶空间的第一个节点,判断是树节点还是普通节点,若为树节点,则调用putTreeVal() 方法插入树,否则新建一个普通节点,并循环找到链表中最后一个节点并判断元素是否存在,若存在则跳转到4;找到链表末尾,将新节点插入到最后一个节点后,判断是否需要树化,若需要树化则调用treeifyBin() 方法进行树化。
  4. 判断在步骤3中是否插入了节点,若无插入新节点,则返回旧值,结束插入逻辑。
  5. hashMap实例的size属性加1,并判断是否需要扩容,若需要扩容则执行resize() 方法
  6. 返回null;

如果喜欢这篇文章,可以给作者评个份哦~

原文声明: "转载本站文章请注明作者和出处Nothinglin ,请勿用于任何商业用途"

公众号:苦逼的学生仔