HashMap之流程分析

HashMap源码分析

HashMap其中最关键的就是他的put方法,你如果真的理解了put方法的全过程,hashMap你可能就理解了大半。

1. Put流程认识全过程

先将put的源码贴出来了,方面大家进行对照。

在这里HashMap的put流程我先不把绘制的流程图贴出来,自己进行debug的流程来分析。 HashMap,顾名思义,肯定是和hash表有关系的。接来下先看看 HashMap的hash函数。

HasMap debug流程

调用普通方法,传入key 和 value,调用 一个 hash方法

hash方法

这段代码的意思就是:当key==null返回0,负责调用hasCode方法和他本身的低16进行一个异或操作。拿到我们的第一个 hash值可能不太理解为什么这么做,请看本专栏的Hash函数部分会有相应解答。这里我们就将他当成一个特殊的哈希函数就可以。(因为上面已经贴出图片,下面统一使用代码块的方式进行)

putval方法的第一步

java 复制代码
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

Node数组就是HashMap存储数据的容器。第一个if,如果 Node数组为null或者,Node数组长度为0,对数组进行扩容。

第二步

java 复制代码
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

(n-1)&hash 他的含义和 hash%n是一样的 ,n是数组长度,这里我认为,这是HashMap第二个hash函数,到这里我们需要理解:通过第一个高低16位异或操作,拿到我们第一个Hash值,在从一个hash值通过第二个hash函数定义为 Node数组中的位置。当前位置为null,直接插入元素。

第三步

java 复制代码
else { ,,,,}

前两个条件都不符合,看看他是如何操作的。

  1. 总是检查第一个
java 复制代码
if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;

在HashMap的源码中,它总是检查第一个元素, 主要代码: (k=p.key)==key,当前节点的key和传入的key如果相等,先记录当前节点。

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

如果说他不在第一个元素上,判断当前节点是不是树,如果是树把当前节点插入树中。(具体细节不详细解读,可以自行观看)。

java 复制代码
else {
    for (int binCount = 0; ; ++binCount) { 
        if ((e = p.next) == null) {  //判断到达尾节点。
            p.next = newNode(hash, key, value, null); //根据当前元素创建节点插入尾部
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //插入玩之后判断是否到达树化的条件,到达树化的条件,进行树化。
                treeifyBin(tab, hash);
            break;
        }
        //判断链表中有没有 相同的key值,如果有直接跳出循环。
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            break;
        p = e;  // 使链表前移。
    }
}

最后,上面的情况都不符合,就到了链表的情况, 总结大概流程:遍历链表

  • 遍历链表中:如果有相同的key,不做任何操作
  • 如果链表中没有相同的key,将该元素插入链表尾部,之后判断是否达到树化的条件,如果达到树化的条件,进行树化操作。
java 复制代码
if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

仔细分析,e!=null的情况是,链表中有相同的key的情况和第一个元素key相同时,总之,就是有相同Key的时候,我们去覆盖 对应的value值,不做任何操作。

java 复制代码
++modCount;
if (++size > threshold)  //
    resize();
afterNodeInsertion(evict);
return null;

最后,我们要判断是否达到扩容条件,如果达到扩容条件,就要进行扩容。接下来就自然而然能画出标准的流程图。

###总结:流程图

相关推荐
80530单词突击赢24 分钟前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法44 分钟前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
WeiXiao_Hyy1 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇1 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
long3161 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
rannn_1112 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
短剑重铸之日2 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
Dragon Wu3 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
一个有梦有戏的人3 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
爬山算法4 小时前
Hibernate(88)如何在负载测试中使用Hibernate?
java·后端·hibernate