HashMap中的put()方法

一. HashMap底层结构

HashMap底层是由哈希表(数组),链表,红黑树构成,哈希表存储的类型是一个节点类型,哈希表默认长度为16,它不会每个位置都用,当哈希表中的元素个数大于等于负载因子(0.75)*哈希表长度就会扩容到原来的2倍

二. 底层的一些常量

三. HashMap的put()方法

当插入一个元素时,先利用hash(key)计算该元素的hash值 

 

这是计算哈希值的具体实现,如果该对象为空哈希值为0,否则调用这个对象重写的hashCode方法在和其向右无符号右移16位做异或运算,这样做的目的是减少hash冲突 

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {

        //tab是底层的哈希表,p是要遍历链表或红黑树的一个引用,n是哈希表长度
        Node<K,V>[] tab; Node<K,V> p; int n, i;

          
        //如果哈希表为空或哈希表的长度位0,则调用resize()方法初始化哈希表,并将长度赋给n
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

        //用(n-1)&hash来定位该元素要存放在哈希表中的哪个位置
        //如果该位置为空,则表示之前没有存放过元素,不存在元素重复的情况,直接放入该位置即可
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

        //否则,该位置不为空,表示之前存放有元素
        else {
            //e是用来标记重复元素的
            Node<K,V> e; K k;

            //这里是判断是否是重复元素,只判断第一个位置是否是重复元素
            //p表示哈希表中的元素
            //如果两者的hash值相同且两个元素的地址相同,那么肯定是重复元素
            //如果两者的hash值相同,地址不同,但equals()比较的内容相同,则是重复元素
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))

                //记录重复元素
                e = p;

             //否则如果第一个元素不是重复元素,且p的类型是红黑树结构,则调用红黑树的put方法进行插入
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

            //否则第一个元素不是重复元素且hash表中是链表结构
            //一次遍历链表后面的元素,看是否还有重复元素,有就标记,到尾部没有就插入
            else {
                for (int binCount = 0; ; ++binCount) {
                      //判断p的下一个是不是空,并将p的下一个赋值给e
                    if ((e = p.next) == null) {
                          
                         //找到链表尾部进行插入
                        p.next = newNode(hash, key, value, null);
                          
                         //binCount用来记录链表中的元素个数,插入后判断链表个数
                         //如果大于等于7(这里从0开始,其实实际个数超过8),则链表转为红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1)
                            treeifyBin(tab, hash);
                        break;
                    }


                    //如果当前要比较的元素(e)不是尾部元素,则判断是否是重复元素
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;//e是重复元素就跳出循环

                    p = e;//不是重复元素就将e赋值给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();//如果哈希表中的元素大于负载因子(0.75)*哈希表长度,则哈希表进行扩容
        afterNodeInsertion(evict);
        return null;
    }

 

当哈希表中某个位置的链表个数大于8,则调用treeifyBin但此时不一定树化,还有一个条件,当哈希表的长度大于MIN_TREEIFY_CAPACITY(64)时才树化,否则只是调用resize()进行扩容

四. 底层哈希表的扩容

1.当向哈希表为null或哈希表的长度为0,即哈希表还没有初始化时会扩容为默认长度16

2.当哈希表中的位置被用了负载因子(0.75)*哈希表的长度时会扩容为原来的2倍

3.当哈希表中某个链表的个数达到8,但哈希表的长度未到64会扩容为原来的2倍

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值