哈希链表转红黑 -- HashMap

一:版本变更

在这里插入图片描述
HashMap在1.8之前采用数组 + 链表组合作为底层数据结构。1.8中针对解决Hash冲突的链表优化加入了红黑树的转换,即在链表长度达到限定阈值后将转换为红黑树结构。链长过长场景下遍历性能相对于链表会得到提升。本文将从元素插入、扩容两个方面尝试分析HashMap

二:重要属性
public class HashMap<K,V> extends AbstractMap<K,V>
 implements Map<K,V>, Cloneable, Serializable {
    // 无参构造、默认数组初始化大小
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;   
    // 默认节点元素最大容量值
    static final int MAXIMUM_CAPACITY = 1 << 30; 
    // 默认填充因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    // 链表转红黑树位桶节点数量阈值
    static final int TREEIFY_THRESHOLD = 8; 
    // 红黑树转链表位桶节点数量最小值
    static final int UNTREEIFY_THRESHOLD = 6;
    // 链表转红黑树结构时桶中最小节点数量
    static final int MIN_TREEIFY_CAPACITY = 64;
    // 存储元素的数组,总是2的幂次倍
    transient Node<k,v>[] table; 
    // 存放节点总数
    transient int size;
    // 每次扩容和更改map结构计数器
    transient int modCount;   
    // 临界值 当实际大小(容量*填充因子)超过临界值时进行扩容
    int threshold;
    // 填充因子
    final float loadFactor;
}
三:链表节点

在这里插入图片描述
链表节点由HashMap维护的内部类Node实现,该内部类定义四个属性

  • hash:key值经过hash运算后产生的值,key相等与hash相等为充分不必要条件
  • key:插入节点的key值
  • value:插入节点的value值
  • next:通过next域维护形成单向链表结构
四:红黑树节点

在这里插入图片描述
红黑树节点与链表节点一致,HashMap维护内部类TreeNode实现,具备以下五个属性

  • parent:记录父节点
  • left:左节点
  • right:右节点
  • prev:预计下一节点
  • red:红黑节点标志
五:元素添加
5.1 流程示意图

具备以上关键属性、内部类知识后基本查看插入代码没有任何压力,都是比较简单的操作。个人感觉这个源码写得很恶心,逻辑不是很专一清晰,需要细心慢慢整理即可
在这里插入图片描述

5.2 重要节点提示

在这里插入图片描述
在这里插入图片描述
以上两个地方会涉及到扩容操作,第一点是当插入时发现Node[] table数组未初始化,第二点是当插入元素后判断节点数量大于阈值。透过第一点情况其实就很清晰位桶数组采用延时加载策略,即使用再加载

在这里插入图片描述
很多地方都会出现这个判断,前面说过,hash值相等key不一定相等。但key相等则其hash值一定相等
在这里插入图片描述
链表转红黑树操作在遍历链表且非覆盖原节点value时发生,仔细看前面的流程图就会发现插入时判断的层次为

  1. 位桶存在节点
  2. 位桶节点类型为树结构
  3. 循环链表判断key覆盖
六:扩容

扩容操作其实可以分为两部分阅读,第一部分是通过算法计算得出扩容后新数组大小及阈值,第二部分则是当扩容前存在节点则重新经过hash计算后加入新结构中

6.1 容量及阈值计算

在这里插入图片描述
首先根据代码可以知道在扩容resize()中夹杂了许多逻辑,可以说每一个分支都是在进行不同的操作

  • 第一个分支oldCap即原容量大于0,若超过容量阈值则不扩容,若原容量大于16且扩容两倍后小于最大容量则扩容两倍。这个算法属于比较正常主干的分支
  • 第二个分支针对初始化构造传入初始化容量的情况,当传入初始化容量小于16扩容时就会走这个分支,将计算的阈值用于新数组大小
  • 第三个分支一看就是无参构造的延迟加载,默认数组大小16,阈值为16 * 0.75
6.2 元素转移

关于元素转移在JDK1.8我就提出一点,节点复制后的要么在原位置亦或是两倍的位置。如果两个节点原位置在一个桶,移动后还在一个桶,这两个节点的顺序不会发生改变。这个相对于JDK1.7的改变也解决了死循环的情况,细节请自行查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值