HashMap的put过程

HashMap的put过程

1、计算hash,调用putVal

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

首先会计算出key的hash数值,然后调用putVal函数。

2、putVal函数

这个函数我按照对应的if-else,把它分为如下几个部分:

  1. 如果表空:(tab = table) == null || (n = tab.length) == 0,这个时候当然是进行一些表的扩大,用resize()方法。
  2. 如果我们把我们待插入的键值对(这里将这个键值对用<hash,key,value>表示)的位置用散列函数散列到表中,并且发现这个表的该位置(p)是null的,说明之前没有出现过这个key,所以直接创建一个新节点,放入这个位置(i)。
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

这个地方稍微举一个例子,比如我们当前tab是一个长度n为4的表,其中0和1的位置有数据,2和3的位置是空,当前插入了一个key的hash为2,v为x的键值对,那么利用上面的散列计算方式((n-1)&hash)我们算的其应该插入的位置为i=2,而我们发现(p=tab[2])==null,所以可以直接创建一个新的节点存到表中。其实这里之所以用&,是因为我们在hashmap中长度规定为2^n,所以&相当于取余。

  1. 如果这个位置有元素,那么我们需要做的工作其实就是找到这个元素应该插入的位置。因为hashmap解决hash冲突的时候采用的是拉链法,所以这个元素要么是附在这个拉链的末端,要么在拉链中找到了一个同样的元素,需要更新对应v。所以下面又有如下步骤。

    3.1. 首先看这个p的key是否和我们待插入的键值对是相同的,这里的判断方法是:

    p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k)))
    

其实就是先看hash值,如果hash值同的话,再看具体数值。这样可以减少equals计算次数。如果我们计算发现p刚好和待插入的元素一样,那么就先用e记录这个位置。

​ 3.2. 因为p其实相当于拉链的头,其实可以看作是一个链表的表头,我们刚才就是判断了一下链表的表头是否和插入的同,如果不同的话,我们就要去链表表里面找了,在链表里面找则有两种情况,一种是找到了,一种是没有找到。不过源码中在进行链表搜索前还有如下一步:

else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

这个其实是jdk1.8的一个优化,如果我们链表过长,那么顺序搜索会影响性能,所以会转换成红黑树。所以这里先看p是不是TreeNode,如果是,则用对应方法插入。

​ 3.3. 如果是正常链表,那么就开始了一个for循环,这个for循环里主要是遍历这个链表,从for里面第一句if ((e = p.next) 和最后一句p =e可以看出主要就是一个遍历。而里面涉及了两个判断,第一个是判断了if ((e = p.next) == null),如果是为null,那么代表链表到达了尾部,那么我们做的就是在链表尾部插入一个新节点即可。另外一个判断是

if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k))))

这个主要判断遍历的这个节点是否和我们待插入的数据相同,如果相同,那么说明里面有重复的节点,它做的操作就是保存这个节点即可(也就是对应的e),这个e和3.1里面的那个e刚好是统一起来,都是代表如果找到了一个完全一样的节点。

再3.2和3.3遍历的过程中还有一个binCount,这个主要记录链表长度,如果长度超过了TREEIFY_THRESHOLD - 1,那么就将链表转为红黑树。

最后就再判断一下e是否为null,如果不是null,那么表示里面找到了一个完全和待插入key一样的元素,那么我们只需要更新v即可。

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值