HashMap之Put(笔记七)

本文详细分析了HashMap的put方法流程,从源码角度解释了put操作如何处理键值对,包括碰撞处理、红黑树转换以及扩容机制。文章还讨论了HashMap的存储结构从链表到红黑树的优化,以及在遇到碰撞时如何处理,特别是当链表长度超过8时转为红黑树的过程。最后,文章提到了HashMap在JDK1.8中的优化,如扩容策略和红黑树的使用,以提高性能。
摘要由CSDN通过智能技术生成

HashMap之put方法流程解读

HashMap的put方法算是HashMap中比较核心的功能了,复杂程度高但是算法巧妙,同时在上一版本的基础之上优化了存储结构,从链表逐步进化成了红黑树,以满足存取性能上的需要。本文逐行分析了put方法的执行流程,重点放在了对整个流程的把握,对于红黑树的执行逻辑只是点到为止,其实HashMap中还有很多细节算法值得分析和学习,本文没有涉及,算是抛砖引玉吧,后面抽空把其他的地方分析一番。

源码阅读与分析

1、HashMap的put方法,翻看源码:

 1 /**
 2  * Associates the specified value with the specified key in this map.
 3  * If the map previously contained a mapping for the key, the old
 4  * value is replaced.
 5  *
 6  * @param key key with which the specified value is to be associated
 7  * @param value value to be associated with the specified key
 8  * @return the previous value associated with <tt>key</tt>, or
 9  *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
10  *         (A <tt>null</tt> return can also indicate that the map
11  *         previously associated <tt>null</tt> with <tt>key</tt>.)
12  */
13 public V put(K key, V value) {
14     return putVal(hash(key), key, value, false, true);
15 }

2、紧接着调用内部方法putVal:

 1 /**
 2  * Implements Map.put and related methods
 3  *
 4  * @param hash hash for key
 5  * @param key the key
 6  * @param value the value to put
 7  * @param onlyIfAbsent if true, don't change existing value
 8  * @param evict if false, the table is in creation mode.
 9  * @return previous value, or null if none
10  */
11 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                boolean evict) {
13     Node<K,V>[] tab; Node<K,V> p; int n, i;
14     if ((tab = table) == null || (n = tab.length) == 0)
15         n = (tab = resize()).length;
16     if ((p = tab[i = (n - 1) & hash]) == null)
17         tab[i] = newNode(hash, key, value, null);
18     else {
19         Node<K,V> e; K k;
20         if (p.hash == hash &&
21             ((k = p.key) == key || (key != null && key.equals(k))))
22             e = p;
23         else if (p instanceof TreeNode)
24             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
25         else {
26             for (int binCount = 0; ; ++binCount) {
27                 if ((e = p.next) == null) {
28                     p.next = newNode(hash, key, value, null);
29                     if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
30                         treeifyBin(tab, hash);
31                     break;
32                 }
33                 if (e.hash == hash &&
34                     ((k = e.key) == key || (key != null && key.equals(k))))
35                     break;
36                 p = e;
37             }
38         }
39         if (e != null) { // existing mapping for key
40             V oldValue = e.value;
41             if (!onlyIfAbsent || oldValue == null)
42                 e.value = value;
43             afterNodeAccess(e);
44             return oldValue;
45         }
46     }
47     ++modCount;
48     if (++size > threshold)
49         resize();
50     afterNodeInsertion(evict);
51     return null;
52 }

拆解源码进行解析

1、put方法的注释

我们先把源码中的注释大致翻译一遍:

1 Associates the specified value with the specified key in this map.
2 If the map previously contained a mapping for the key, the old
3 value is replaced.
4 @param key key with which the specified value is to be associated
5 @param value value to be associated with the specified key
6 @return the previous value associated with <tt>key</tt>, or
7         <tt>null</tt> if there was no mapping for <tt>key</tt>.
8         (A <tt>null</tt> return can also indicate that the map
9         previously associated <tt>null</tt> with <tt>key</tt>.)

大意为:将指定的值与此映射中的指定键相关联,如果Map中已经包含了该键的映射,那么旧的映射值将会被替代,也就是说在put时,如果map中已经包含有key所关联的键值对,那么后续put进来的键值对,将会以相同key为准替换掉原来的那一对键值对。

返回的值则将是之前在map中实际与key相关联的Value值(也就是旧的值),如果key没有实际映射值的话那就返回null。

put方法作为对外暴露的方法,在内部实现时则立马调用了其内部putVal方法,并将put进去(覆盖)之前的结果k-v中的v进行了返回,但map中最新绑定的那一对k-v中的v已经是最新put的了。

1 /**
2 * 对外暴露的put方法
3 **/
4 public V put(K key, V value) {
5     return putVal(hash(key), key, value, false, true);
6 }

2、putVal方法中的第一个参数hash

我们先把源码中的方法上面的注释先浏览一遍:

 1 Computes key.hashCode() and spreads (XORs) higher bits of hash
 2 to lower.  Because the table uses power-of-two masking, sets of
 3 hashes that vary only in bits above the current mask will
 4 always collide. (Among known examples are sets of Float keys
 5 holding consecutive whole numbers in small tables.)  So we
 6 apply a transform that spreads the impact of higher bits
 7 downward. There is a tradeoff between speed, utility, and
 8 quality of bit-spreading. Because many common sets of hashes
 9 are already reasonably distributed (so don't benefit from
10 spreading), and because we use trees to handle large sets of
11 collisions in bins, we just XOR some shifted bits in the
12 cheapest possible way to reduce systematic lossage, as well as
13 to incorporate impact of the highest bits that would otherwise
14 never be used in index calculations because of table bounds.

大意为:将key的hashcode值(由native方法计算得到)再与该值的高16位进行异或运算得到最终的hash值。这样做的目的作者也给出了解释,就是通常的hash算法都总是碰撞,我们这样做的目的尽量使得hash值较为分散。(大概理解)

3、开始解析putVal里面的方法

 1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值