首页 > 编程语言 >java中HashMap的设计精妙在哪?

java中HashMap的设计精妙在哪?

时间:2022-10-27 16:48:19浏览次数:66  
标签:扩容 精妙 java HashMap 插法 链表 哈希 节点

摘要:本文结合图解和问题,教你一次性搞定HashMap

本文分享自华为云社区《java中HashMap的设计精妙在哪?用图解和几个问题教你一次性搞定HashMap》,作者:breakDawn。

HashMap核心原理

HashMap完整的put过程

以下是对上图的详细解释:

  1. 首先,要获取key的哈希值。
    如果为空,就统一是0
    否则,调用对象的.hashCode()方法,接着再与自己的右移16位进行异或,以便充分利用高位信息。
  2. 接着判断内部node数组是否为空,如果是,先进行初始化扩容。默认为16。
  3. 根据(n-1)&hash值,获取哈希表索引位置。
  4. 哈希表的node数组中,存放的是每组链表的头节点。
    先检查头节点是否和自己要存放的key完全匹配 (hash值相同,key值相同,先hash再key,是因为hash的判断简单,key的equals判断可能会复杂)。如果匹配,得到需要替换的节点。
  5. 头节点和自己要放的key不匹配,则判断一下这个头节点是否是红黑树节点,如果是,说明已经升级成红黑树了,调用putTree插入到红黑树中。
  6. 如果不是红黑树, 那就是遍历链表,完全匹配就得到需要替换的节点。如果到尾部了,也没匹配的,则插入新节点。
  7. 如果前面找到了要替换的节点,则判断一下是否可以替换(是否没要求putIfAbsent,或者value为null),是就替换,不是就结束
  8. 如果前面是插入了新节点,非替换, 则要modCount++(方便迭代器确认map是否更新), 同时++size, 然后和扩容阈值做判断, 如果太大,就resize进行扩容

hashMap的扩容过程,java7和8扩容的区别

java7:

  • 当resize时,新建一个数组newTable
  • 遍历原table中的每个链表和节点,重新hash,找到新的位置放入
  • 放入的方式是头插法,即始终插在链表的头节点。

java8:

  • 不再每个点rehash放置,而是最高位是0则坐标不变,最高位是1则坐标变为“10000+原坐标”,即“原长度+原坐标. 避免了频繁的哈希计算和搬移过程。
  • 使用尾插法在链表上插入节点
  • 桶内元素超过8个,链表转成红黑树

为什么java8要改成尾插法?

A:多线程时,java7的map-put可能造成死循环。
A线程扩容到那一半, 还处在遍历链表做头插法搬移的过程时,存了2个局部变量,当前链点now指向a, next指向b,正准备搬移(a->b->c这样的链表,a是头节点)

B线程则同时完成线程扩容,但是map里都是引用,浅拷贝,** 因为是头插法, 会导致顺序变化**, 原本a->b->c 变成了c->b->a。
因此A恢复时, 链点还是a,next还是b, 于是往下走到了b, 取bbs的next时,已经变成了a, 于是发生了a->b->a的循环
导致后续操作的next都是错误操作,引发环形指针。

java8里改成尾插法,这样做resize时,a->b->c 如果仍然哈希到同一个节点, 顺序是不会发生变化的。

虽然解决了死循环问题, 但java8的hashMap仍然是线程不安全的,为什么?

A:因为缺乏同步,导致同节点发生哈希碰撞时,if条件的判断都可能是有问题的,导致本该插在链表头节点后面的,结果直接作为链表头覆盖到数组上了。

具体到底满足什么情况,才会resize扩容呢?

A:HashMap负载因子 LoadFactor,默认值为0.75f。
衡量HashMap是否进行Resize的条件如下:
HashMap.Size >= Capacity * LoadFactor

另一种情况。JDK1.8源码中,执行树形化之前,会先检查数组长度,如果长度小于64,则对数组进行扩容,而不是进行树形化

扩容后,capacity扩容多少倍呢?为什么

A:哈希表每次扩容是两倍。
初始长度为2的幂次方,随后以2倍扩容的方式扩容,元素在新表中的位置要么不动,要么有规律的出现在新表中(二的幂次方偏移量),这样会使扩容的效率大大提高。
另外,hashmap采用二倍扩容还有另外一个好处:可以使元素均匀的散布hashmap中,减少hash碰撞。

 

点击关注,第一时间了解华为云新鲜技术~

标签:扩容,精妙,java,HashMap,插法,链表,哈希,节点
From: https://www.cnblogs.com/huaweiyun/p/16832670.html

相关文章

  • Java 使用发送请求报错
    问题发送post请求报错javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIXpathbuildingfailed:sun.security.provider.cer......
  • Jenkins java服务更新和回滚
    [root@jenkinsscripts]#catjava_deploy_tag_rollback.sh#!/bin/bashDATE=$(date+%Y-%m-%d-%H-%M-%S)web_server="192.168.220.143192.168.220.144"Sdir=/optDd......
  • 【Java】线程的死锁
    1.死锁不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。说明:出现死锁后,不会出现异常,不会出现提示,只是所有的线程......
  • VS Code 配置JAVA环境
    1.首选添加如中文不好可先添加中文语言包,2.添加DebuggerforJava3.添加ExtensionPackForJava4。添加LanguageSupportfor Java至此,简单的学习环境已可以 ......
  • Java8新特性3:Stream流
    回顾之前《JavaSE-23.2》:https://www.cnblogs.com/yppah/p/14900824.htmlhttps://www.cnblogs.com/yppah/p/14900829.htmlhttps://www.cnblogs.com/yppah/p/14900834.ht......
  • Java多线程(4):ThreadLocal
    您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~​为了提高CPU的利用率,工程师们创造了多线程。但是线程们说:要有光!(为了减少线程创建(T1启动)和销毁(T3切换)的时间),于是工程师们......
  • 【idea】创建一个java项目
    1、文件-新建-项目-选择java模块选择SDK   2、下一步   3、输入项目名称和项目位置,点击完成  4、打开新建项目     5、在src目录......
  • 使用 WxJava 发布公众号图文
    一个Demo,记录一下发布公众号图文时需要用到的接口公众号开发时需用到的一些网站微信官方文档平台,开发公众号只用查看公众号那一块微信公众平台接口测试帐号申请,申......
  • JAVA---4种内部类
    1.局部内部类java类的五个特性:属性,方法,构造器,代码块,内部类 2.匿名内部类  3.成员内部类    4.静态内部类       小结 ......
  • 基于Java websocket的公共聊天程序
    实验中使用的是tomcat的websocket,由于程序部署到apache-tomcat-8.5.24上,所以只需额外添加消息Json解析包:json-org。实际使用中注意修改目标地址:ws://localhost:8080/GameDem......