常见方法源码分析:HashMap的put(K k,V v)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见方法源码分析:HashMap的put(K k,V v)相关的知识,希望对你有一定的参考价值。

version:JDK1.8

一.调用内部的另一个方法:

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

将结果直接返回

源码:

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

二.那么进入putVal这个方法看看内部是怎么实现的

注:当前对象就是需要插入的HashMap对象

1.首先声明了四个变量

1 Node<K, V>[] tab; //当前对象HashMap中的所有键值对Node对象数组
2 Node<K, V> p; //传入key在当前对象中对应的Node对象
3 int n; //当前对象内部Node的数量
4 int i; //传入ley在当前对象中对应的Node对象的数组下标

2.判断当前对象是否为null或者内部没有Node,

如果是,那么给当前对象初始化一下,并且对tab和n进行赋值

1 if ((tab = table) == null || (n = tab.length) == 0)
2     n = (tab = resize()).length;

3.判断key对应的Node在当前对象中是否存在

-> 如果不存在,那么新建一个Node,并插入到对应的Node数组中

1 if ((p = tab[i = (n - 1) & hash]) == null)
2     tab[i] = newNode(hash, key, value, null);

其中的newNode方法如下:

1 Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
2     return new Node<>(hash, key, value, next);
3 }

-> 如果存在

1).声明两个变量:

1 Node<K, V> e; 
2 K k; 

2).进行判断,key值在当前对象中对应的Node的hash值和key是否一样

--> 一致

将p赋值给e

1 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
3     e = p;

--> 不一致,说明当前的HashMap中已经存在一个Node占了这个坑,继续判断占坑的Node是否为TreeNode:

--> 属于TreeNode,那么进行转换:

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

--> 不属于,那么就需要把这个坑里的所有Node都拿出来,看看有没有和传入的key和hash值一样的

开始循环,知道最后一个对比结束或者找到了和传入的hash值和key一样的Node

到最后也没有找到node的话,生成一个新的node,

后面的操作不明白,等以后知道再编辑修改

 1 for (int binCount = 0; ; ++binCount) {
 2    if ((e = p.next) == null) {
 3        p.next = newNode(hash, key, value, null);
 4        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
 5            treeifyBin(tab, hash);
 6            break;
 7        }
 8        if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
10            break;
11        p = e;
12 }

最后判断是否找到了这个node,如果找到了,取出这个node原来的value

如果需要替换或者原来的node中的value是为null的,那么把传入的value赋值给这个node

最后把原来的value值返回,方法结束

1 if (e != null) { // existing mapping for key
2     V oldValue = e.value;
3     if (!onlyIfAbsent || oldValue == null)
4         e.value = value;
5     afterNodeAccess(e);
6     return oldValue;
7 }

里面的afterNodeAccess(e),并没有进行任何操作:

1 // Callbacks to allow LinkedHashMap post-actions
2 void afterNodeAccess(Node<K,V> p) { }

最后,map的修改次数++,若是map的长度和原来的不一致,也重新设定长度,执行到这里,说明之前并没有成功插入,那么返回null:

1 ++modCount;
2 if (++size > threshold)
3     resize();
4 afterNodeInsertion(evict);
5 return null;

最后,完整的源码如下:

 1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                boolean evict) {
 3     Node<K,V>[] tab; Node<K,V> p; int n, i;
 4     if ((tab = table) == null || (n = tab.length) == 0)
 5         n = (tab = resize()).length;
 6     if ((p = tab[i = (n - 1) & hash]) == null)
 7         tab[i] = newNode(hash, key, value, null);
 8     else {
 9         Node<K,V> e; K k;
10         if (p.hash == hash &&
11             ((k = p.key) == key || (key != null && key.equals(k))))
12             e = p;
13         else if (p instanceof TreeNode)
14             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
15         else {
16             for (int binCount = 0; ; ++binCount) {
17                 if ((e = p.next) == null) {
18                     p.next = newNode(hash, key, value, null);
19                     if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
20                         treeifyBin(tab, hash);
21                     break;
22                 }
23                 if (e.hash == hash &&
24                     ((k = e.key) == key || (key != null && key.equals(k))))
25                     break;
26                 p = e;
27             }
28         }
29         if (e != null) { // existing mapping for key
30             V oldValue = e.value;
31             if (!onlyIfAbsent || oldValue == null)
32                 e.value = value;
33             afterNodeAccess(e);
34             return oldValue;
35         }
36     }
37     ++modCount;
38     if (++size > threshold)
39         resize();
40     afterNodeInsertion(evict);
41     return null;
42 }

最后:

    编写源码的人的喜欢把赋值写在判断中的风格有点接受不能

以上是关于常见方法源码分析:HashMap的put(K k,V v)的主要内容,如果未能解决你的问题,请参考以下文章

hashmap的put方法源码分析

2HashMap源码分析

JavaHashMap源码分析——常用方法详解

HashMap中 get(K key) 和 put(K key,V value) 的具体过程

手撕源码之HashMap

手撕源码之HashMap