重识HashMap(二)

前言:没想到短短的一个put方法,竟然有这么多逻辑。。。

Hello,昨天大概讲了下HashMap中几个重要的默认值以及HashMap的构造函数,今天主要想看一下存储数据的过程,即HashMap中put方法的实现。

public V put(K key, V value) {
	//1,先将key进行hash扰动
	//2,调用putVal方法
   return putVal(hash(key), key, value, false, true);
}

//存储所有元素的数组,默认为null
transient Node<K,V>[] table;

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; 
        Node<K,V> p;
        int n, i;
        
        //判断table如果为空,调用resize方法初始化table
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        
        //(n - 1) & hash 计算出来的值是在0->n之间的数,即不会超过数组的长度
        //如果当前数组下标的数据为空,则将当前元素直接存放到i下标位置
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //1,如果table[i]位置已经存在元素,
            //则判断当前传入的key与当前i位置元素的key是否完全一致,如果一致,
            //则直接运行到后面去覆盖value值
            //如: map.put("1","1"); 后再次:map.put("1","2");
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))){
                e = p;
            } else if (p instanceof TreeNode){
            	//2,判断当前table[i]位置的元素如果它已经是树结构,则当前key,value就需要存入到TreeNode中
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            }else {
            	//3,最后就只有链表这种情况了,那就循环遍历链表吧
                for (int binCount = 0; ; ++binCount) {
                	//如果找到链表的最后一个元素,即p.next == null
                	//那就在链表最后追加一个新节点
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //判断链表长度是否大于等于8,如果是就要进行树化
                        if (binCount >= TREEIFY_THRESHOLD - 1){
                            treeifyBin(tab, hash);
                        }
                        break;
                    }
                    //在遍历过程中,判断链表中元素的key,
                    //如果与当前传入的key相同,则会跳出循环
                    //注意循环中的e = p.next,此处跳出循环后e仍旧是有值的
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))){
                        break;
                    }
                    //继续下一个链表元素进行判断
                    p = e;
                }
            }
            //这里就是value值覆盖操作啦
            //如果有在数组中找到元素,则需要将当前传入的value覆盖旧的oldValue
            if (e != null) { 
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //如果table的长度超过了容器设置的扩展阈值,就会进行扩容
        if (++size > threshold){
            resize();
        }
        afterNodeInsertion(evict);
        return null;
    }

代码可能看得有点晕,画一个顺序图吧:
在这里插入图片描述

总结:今天大致梳理了一下put方法的执行过程。但依旧只是皮毛,没有深究到树化方法的逻辑(treeifyBin),以及树结构put方法存储value的逻辑(putTreeVal),今天就到这吧。我是阿雷,一个逐渐熬夜的程序员。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值