理解HashMap(jdk8)[通俗易懂]

理解HashMap(jdk8)

大家好,又见面了,我是你们的朋友全栈君。

HashMap 数据结构

616953-20160304192851940-1880633940.png

 图中的 “table” 在 HashMap 中是一个 Node<K,V> 数组 。HashMap 内部数据结构是由数组链表树形结构组合而成的。

 

什么是hash?

 百度百科:hash 一般被翻译为 “散列”, 也有直接音译为“哈希”的,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

wikipedia : 

哈希函数 : 哈希函数就是能将任意长度的数据映射为固定长度的数据的函数。哈希函数返回的值被叫做哈希值、哈希码、散列,或者直接叫做哈希。一个使用场景就是哈希表,哈希表被广泛用于快速搜索数据。

哈希表:哈希表是一种能实现关联数组的抽象数据结构,能把很多「键」映射到很多「值」上。哈希表使用哈希函数来计算索引,一个索引对应一个值。

 

HashMap 初始化

// initialCapacity 初始化容量大小
// loadFactor 负载因子
public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        // threshold 是HashMap是否要进行扩容的标记量
        this.threshold = tableSizeFor(initialCapacity);
    }

initialCapacity , loadFactor  这两个参数都影响着HashMap的性能。initialCapacity 决定了下一次 resize 后的容量(capacity << 1) ,  loadFactor  决定了 resize 发生的条件 (size > (capacity * loadFactor )) (一般情况下 , 极端情况下是 size > Integer.MAX_VALUE) 。如果初始化时不指定这两个参数,会使用默认值 , capacity  = 16 , loadFactor  = 0.75 。对于 16 的容量空间,如果不能充分利用的话会造成空间资源的浪费。(个人认为高手之所以高都体现在细节之中)

 

散列过程

散列的过程就是将存入的元素均匀的分布到HashMap内部Node数据的过程。均匀分布指的是 , 数组中的每个位置尽量都存储了一个Node节点,并且该位置上的链表只有一个元素。散列分布的越均匀进行碰撞检测的次数就越少,查询性能就越高。

这就是一个散列较为均匀的 , 查询时最好情况下可以直接定位 , 最坏情况下需要进行一次碰撞检测。

理解HashMap(jdk8)[通俗易懂]

 

这是一个散列的不均匀的,查询时会进行多次碰撞检测造成效率较低。

理解HashMap(jdk8)[通俗易懂]

 

碰撞检测

((capacity – 1) & hash) 会计算出 key 存储在 Node 数组中的那个位置上 (得到的值始终会落在 Node数组的长度范围内 , 等同于 hash % capacity  , 不过位运算的效率更高), 如果发现该位置上已经存在Node 了,会将新存入的数据作为链表的尾节点。这样存储和查询时都会进行碰撞检测。碰撞检测其实就是比较传入的 key 的 hash 与同一 bucket 上所有的 key 的 hash 是否一致的过程。 jdk8 在这方面做了优化,加入了树型结构来弥补链表线性结构性能较低的不足。

提高碰撞检测的性能 , 从代码中也能看出来  , 该运算的最理想情况(hash 相等情况下)是执行两步 , 比较 hash , 比较 key 。 最坏情况是执行 4 步 , 只要取最好情况就达到了提高性能的目的 。以此类推 key 就可以用一些 String , Enum 这之类的数据类型。

if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) {
  // .......
}

 

reszie

rezise 是一个较为消耗性能的过程 , 在首次向HashMap中存入元素的时候会进行首次resize ,  在之后每当产生新节点(这里的节点指的是Node) , 同时 size > threshold 的时候会进行 resize ,resize 的过程也是 rehash 的过程。本篇尽量不分析源码只是做整体的,概念上的了解。

 

并发安全

HashMap 是不支持并发的 , 在并发修改时它采用 fail-fast 的策略 , 抛出 ConcurrentModificationException 。 多线程环境下操作HashMap有可能会造成死循环 , 在 resize 的过程当中。不要在多线程场景下使用HashMap 。

转载于:https://my.oschina.net/j4love/blog/1797058

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107573.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Windows操作系统双因素身份认证解决方案

    Windows操作系统双因素身份认证解决方案Windows桌面帮助企业将办公桌面快速、集中部署在平台上,方便进行管理维护且节省企业成本,能让员工随时随地登录到自己的windows桌面环境中,实现移动办公。安全事件频发的现在,在单一的静态密码登录验证机制下,非法入侵者若窃听到桌面登录账号的用户名及密码,即可通过合法访问权限访问内部系统,企业信息安全面临挑战;企业为防止账号信息泄露,通常强制要求员工定期更换登录密码,给员工及IT运维人员带来许多不必要的麻烦;其次没有及时收回的账号,离职员工仍然有桌面的合法访问权限,因此额外增加了IT部门的账号回收管理

    2025年7月9日
    6
  • golang deepcopy_mongodb主从复制原理

    golang deepcopy_mongodb主从复制原理Go语言中所有赋值操作都是值传递,如果结构中不含指针,则直接赋值就是深度拷贝;如果结构中含有指针(包括自定义指针,以及切片,map等使用了指针的内置类型),则数据源和拷贝之间对应指针会共同指向同一块内存,这时深度拷贝需要特别处理。目前,有三种方法,一是用gob序列化成字节序列再反序列化生成克隆对象;二是先转换成json字节序列,再解析字节序列生成克隆对象;三是针对具体情况,定制化拷贝。前两种方法虽……

    2022年10月2日
    3
  • js漂浮广告代码_JavaScript上传文件代码

    js漂浮广告代码_JavaScript上传文件代码//浮动广告代码varx=50,y=60; //设置元素在浏览器窗口中的初始位置varxin=true,yin=true;//设置xin、yin用于判断元素是否在窗口范围内varstep=1; //可设置每次移动几像素varobj=document.getElementById(“Ad”);//通过id获取div元素functionfloatAd(){varL=T=0;varR=document.body.clientWidth-obj.off

    2026年2月4日
    7
  • js中判断数组中是否包含某元素的方法有哪些_js判断数组里面是否包含某个元素

    js中判断数组中是否包含某元素的方法有哪些_js判断数组里面是否包含某个元素方法一: arr.indexOf(某元素):未找到则返回-1。 实际用法:if(arr.indexOf(某元素)&gt;-1){//则包含该元素}1例:varfruits=["Banana","Orange","Apple","Mango"];vara=fruits.indexOf("Apple");//2//以上输出结果意味着"Apple"元

    2022年10月19日
    7
  • dedecms中{dede:myad name=’about’/} 修改内容

    dedecms中{dede:myad name=’about’/} 修改内容

    2021年9月21日
    29
  • Mariadb 安装教程 Windows版[通俗易懂]

    Mariadb 安装教程 Windows版[通俗易懂]MariadbWindows版安装教程1、下载软件:https://mariadb.org/download/2、双击运行mariadb-10.5.5-winx64.msi,3、点击iaccept…接受许可协议4、选择组件以及软件安装路径5、设置数据库的密码6、默认下一步7、点击install进行安装即可…

    2022年6月13日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号