《别看了,你学不会的》——HashMap底层源码解析

前言

2-3树
  • HashMap在1.8之后使用了红黑树。红黑树是一种自平衡的二叉树,它可以避免二分搜索树在极端情况下蜕变成链表的情况。在学习红黑树之前,首先要了解2-3树
  • 2-3树是一种绝对平衡的多叉树,在这颗树中,任意一个节点,它的左右子树的高度树相同的。
    在这里插入图片描述
  • 2-3树分为两种节点,分别是2-节点,和3-节点。其中,2-节点表示节点中保存一个元素,3-节点则表示节点中保存两个元素。
  • 如何生成一个2-3树
    • 向2-3树中插入30和25
      在这里插入图片描述
    • 当插入39的时候,一个节点就容纳了3个元素了,那么就要进行分裂操作
      在这里插入图片描述
    • 然后再插入20和33,可以正常的容纳这两个插入的元素
      在这里插入图片描述
    • 再继续插入17和43时,有两个节点都出现了容纳3个元素,那么这两个节点都需要进行分裂操作
      在这里插入图片描述
    • 插入27和35,两个节点都可以容纳这两个新插入的元素
      在这里插入图片描述
    • 那么再最后插入22,结果发现,一个节点容纳了3个元素,要进行分裂,但是分裂后,叶子节点的高度不一致,那么就要再进行聚合操作
      在这里插入图片描述
  • 在了解完2-3树之后,回过头来看一下红黑树,也就是说,2-3树怎么转变成红黑树?方式很多,此处可以采用左倾红黑树的方式,来将2-3转换为红黑树,转换规则如下:
    在这里插入图片描述
    如上规则进行转换上面的2-3树时,构造的红黑树如下
    在这里插入图片描述
红黑树
红黑树必须满足以下5个条件:
  • 每个节点要么是红色,要么是黑色
  • 根节点一定是黑色
  • 每个叶子节点一定是黑色
  • 如果一个节点上红色,那么它的左右子节点一定都是黑色的
  • 从任意一个节点到叶子节点,所经过的黑色节点的数量一样多

也许你会发现上面的2-3树转换成红黑树之后33节点是红色的,这并不满足所有叶子节点都是黑色这一条件
其实,那是因为没有画上空的叶子节点,完整的红黑树如下
在这里插入图片描述

HashMap源码解析

当我们使用HashMap的时候,首先会通过HashMap的构造方法创建HashMap,然后通过put方法向HashMap对象赋值

HashMap的构造函数
HashMap的构造方法
    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
   
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

上面代码做的操作是给loadFactor赋值
loadFactor是什么?它是HashMap的加载因子,也就是说,元素所占的空间达到加载因子的规定值的时候,那么就会执行扩容

DEFAULT_LOAD_FACTOR的值为0.75f

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

0.75的含义就是:如果数组中存储的元素长度达到了原长度的75%,那么就需要执行扩容操作

put方法
public V put(K key, V value) {
   
        return putVal(hash(key), key, value, false, true);
    }

put方法里面只是调用了putVal方法

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
   
        Node<K, V>[] tab;
        Node<K, V> p;
        int n, i;
        if ((tab = table) == null || (n = tab.length) == 0) {
   //如果是空的table,那么默认初始化一个长度为16的Node数组
            n = (tab = resize()).length;
        }
        if ((p = tab[i = (n - 1) & hash]) == null) {
   //如果计算后的下标i,在tab数组中没有数据,那么则新增Node节点
            tab[i] = newNode(hash, key, value, null);
            //判断是否有哈希冲突,如果没有,直接向数组赋值
        } else {
   
            Node<K, V> e;
            K k;
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {
   //如果与已存在的Node是相同的key值
                e = p;
            }
            else if (p instanceof TreeNode) {
    //如果与已存在的Node是相同的key值,并且是树节点
                e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
            } else {
    //如果与已存在的Node是相同的key值,并且是普通节点,则循环遍历链式Node,并对比hash和key,如果都不相同,则将新的Node拼装到链表的末尾。如果相同,则进行更新
                for (int binCount = 0; ; ++binCount) {
   
                //获得p节点的后置节点,赋值给e。直到遍历到横向链表的最后一个节点,即:该节点的next后置指针为null
                    if ((e = p.next) == null) {
   
                        p.next = newNode(hash, key, value, null);
                        // binCount从0开始,横向链表中第2个node对应binCount=0,如果Node链表大于8个Node,那么试图变为红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1) {
   
                            treeifyBin(tab, hash);
                        }
                        break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值