HashMap的存储底层源码超详解

🐒个人主页

🏅JavaSE系列专栏

🪄前言:

🐒本篇会简单剖析一下HashMap的源码(源码部分每行代码均有注释),有不对之处,欢迎大佬指正🎇

🎀引子:

请输出重复字符的个数,并用格式{a=3, b=4, c=5, d=6, l=2}输出

        String s="aaabbbbcccccddddddll";
        HashMap<Character,Integer> map=new HashMap<>();
        for (int i = 0; i <s.length() ; i++) {
            char c= s.charAt(i);//取出每个字符
            if(!map.containsKey(c)){//判断字符是否在里面
                map.put( c, 1);
            }else {
                map.put(c, (map.get(c)+1));
            }
        }
        System.out.println(map);

进入🪂hashMap.put()方法🪂内:


在这里插入图片描述在这里插入图片描述

🏅来到HashMap的底层源码putVal()方法

 final V putVal(int hash,  K key,  V value, boolean onlyIfAbsent, boolean evict) {
            //        键的哈希值 ,  键 ,     值   ,         布尔类型,         布尔类型
            HashMap.Node<K,V>[] tab;        HashMap.Node<K,V> p;      int n,         i;
            //定义哈希数组[]--(存储类型是链表);      定义链表节点p ;    数组长度  ,   数组下标
            if ((tab = table) == null || (n = tab.length) == 0)
                //如果数组没有初始化(为空 或     数组长度为0
                n = (tab = resize()).length;
            //   n=(数组进行扩容,默认长度16)
            if ((p = tab[i = (n - 1) & hash]) == null)//【注:这个if是计算判断节点p在数组存放的位置是否为空,i代表数组下标】
                //p=tab[i]  而i=哈希值%数组长度=(n - 1) & hash(这个是位运算=左边公式)
                //如果成立在此处创建节点存储数据
                tab[i] = newNode(hash, key, value, null);
            else {          //点p在数组存放的位置不为空
                HashMap.Node<K,V> e;         K k;
                //定义链表节点e              定义 键k
                if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))//【if作用:当存入相同键名key时,是它会更新值value的原因】
                    //判断之前输入与当前输入键的哈希值是否相等 && 键值地址是否相同 ||当前键值是否为空&&键值内容是否相同
                    e = p;
                //如果键值相同,e会存储新节点p的地址实现value值的更新
                else if (p instanceof HashMap.TreeNode)
                          //判断链表是否为红黑树结构
                    e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                         //按红黑树结构进行存储
                else {//节点连接节点,构成链表
                    for (int binCount = 0; ; ++binCount) {
                        //计算链表中节点的个数
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1)
							// binCount从0开始,binCount>=7(当节点个数>=8个节点时),进入红黑树方法【注:链表不一定转化为红黑树】

                                treeifyBin(tab, hash);
                            //此方法--下面详细讲解【附图片讲解】
                            //【结论:当节点数超过7个并且数组长度>=64时,链表会转化为红黑树来存储】
                            break;
                        }
                        if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

🎇红黑树方法treeifyBin()

 final void treeifyBin(Node<K,V>[] tab,    int hash) {
            //                【链表类型】哈希数组,   键的哈希值
            int n, index; HashMap.Node<K,V> e;
            
            if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                //数组为空   || 数组长度< 64       
                resize();
            //数组进行扩容,不进行红黑树转化!
            else if ((e = tab[index = (n - 1) & hash]) != null) {
                //     上面代码写过:确定节点e在数组中位置,这里证明位置不为空
                HashMap.TreeNode<K,V> hd = null, tl = null;
                do {//下面代码均为链表-->红黑树转化部分
                    HashMap.TreeNode<K,V> p = replacementTreeNode(e, null);
                    if (tl == null)
                        hd = p;
                    else {
                        p.prev = tl;
                        tl.next = p;
                    }
                    tl = p;
                } while ((e = e.next) != null);
                if ((tab[index] = hd) != null)
                    hd.treeify(tab);
            }
        }

🪅思路回顾:

在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信计2102罗铠威

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值