HashMap底层数据结构详细解析

 更新时间:2023年11月18日 10:03:01   作者:智由静生  
这篇文章主要介绍了HashMap底层数据结构详细解析,HashMap作为开发中常用的数据结构,也是面试中经常被问的知识点,因此作为开发者应该尽可能多的理解其底层的数据结构,需要的朋友可以参考下

一、HashMap的底层数据结构

HashMap作为开发中常用的数据结构,也是面试中经常被问的知识点,因此作为开发者应该尽可能多的理解其底层的数据结构。

创建一个HashMap很简单,假设创建一个人员毕业院校的HashMap

Map<String, String> map = new HashMap<>();
map.put(”张三”: “南京大学”);
map.put(“李四”, “西北工业大学”);

你可能以为数据是这样存储的:

{
        “张三”:  “南京大学”,
        “李四”: ”西北工业大学”
}

但其实它的底层是数组,是这样存储的:

[<”张三”, “南京大学”>, <”李四”,”西北工业大学”>]

但元素并不是顺序放入数组的,它的计算方式是:对key值计算出一个hash值,然后用这个hash值对数组长度取模,根据取模计算结果定位到数组的位置。

假设数组长度是16,对”张三”的hash取模计算结果是4,那么它就放在数组的第5个位置上。实际的存储大约是这样:

[<>, <>, <>, <>, <”张三”, “南京大学”>, <>, <>, <”李四”,”西北工业大学”>, <>, <>, <>, <>, <>, <>, <>, <>]

取出元素的计算过程类似,比如map.get(“张三”),先对”张三”计算出一个hash值,然后用这个hash值对数组长度取模,根据模计算结果定位到数组中的位置,将该位置的元素取出。

二、JDK1.8对HashMap算法的优化

1、对寻址算法的优化

由hash值对数组长度n取模运算,改为hash值与数组长度n减1进行与运算,即hash&(n-1)。这两者在数学上,计算结果是等价的,但从计算机角度来说,后者的运算性能要比前者高很多。

2、对hash算法的优化

不是直接用hashcode值进行运算,而是使用了新的算法,以下是jdk1.8的一段源码:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode() ^ (h >>> 16));
}

hashCode()的返回值是一个32位整数,这个算法的意思就是用hashCode()值右移16位后的值与hashCode()原值进行异或运算。由于右移16位后左侧补0,而与0异或的结果是原值,所以hashCode()的高16位不变,因此此运算相当于hashCode()将的高16位与低16位进行异或运算。

例如:假设有如下一个key值

假设数组长度是16,它的计算过程如下:

那么为什么要进行这样的计算呢?

这是因为,数组长度一般比较小,因此,它的高16位一般都是0。0与任何数进行与运算,结果都是0,因此key值的高16位相当于没起作用,因为结果都一样,实际上就只看低16位的计算结果,这样就增加了计算结果重复的概率。从而增加了hash冲突。改与以上优化算法,让高16位也参与了进来,就能一定程度上减少这种冲突。

三、HashMap如何解决hash碰撞问题

无论hash算法如何优化,对不同的key算出来的hash值是有可能相同的,这种情况叫hash碰撞或者hash冲突。

两个不同的元素不可能放到数组的同一个位置。HashMap的解决方法是,在这个位置放一个链表,链表里可以存多个元素,将相同hash值的元素都存放到这个链表中。当通过get方法读取数据时,当定位到这个位置发现是个链表,就对这个链表进行遍历查询,找到需要的元素。

链表遍历查询的时间复杂度是O(N),当链表比较长时,也就是hash冲突比较多时,性能比较差。因此HashMap对此做了优化,当达到一定条件时,就会将链表转为红黑树。红黑树遍历查询的时间复杂度是O(logN),性能有很大提升。

在JDK1.8之后,HashMap中的链表在同时满足以下两个条件时,将会转化为红黑树(即自平衡的排序二叉树):

1. 条件一:数组 arr[i] 处存放的链表长度大于8;

2. 条件二:数组长度大于64。

满足以上两个条件,数组 arr[i] 处的链表将自动转化为红黑树,其他位置如 arr[i+1] 处的数组元素仍为链表,不受影响。

四、HashMap如何进行扩容

HashMap底层是数组,当数组满了之后,它就会自动进行扩容,变成一个更大的数组,扩容方式就是2倍扩容,数量直接翻倍。由于数组长度变了,而数组长度是参与hash运算的,因此扩容后需要重新进行hash运算,这就可能会产生内容的变化。比如原来有hash冲突需要产生链表,但re-hash运算后没有冲突了,不需要链表了。或者某个位置的链表里有三个元素,进行re-hash运算后,可能变成了两个。

五、ConcurrentHashMap实现线程安全的底层原理

ConcurrentHashMap是线程安全的HashMap,两者都继承自AbstractMap。在需要线程安全的场合操作HashMap需要使用synchronized关键字加锁,性能很低。而ConcurrentHashMap本身就是线程安全,无需再加synchronized关键字,且已经做了优化,可以直接使用。

ConcurrentHashMap的数据结构与HashMap基本相同,底层都是数组。JDK1.7以前采用的是分段加锁,底层不是一个数组,而是分成多个数组。写数据时内部还是加锁的,但是只对所在段的数组加锁,不同段的操作互不影响,所以·可以并行操作,提高了性能。

JDK1.8以后进一步优化和改进,和HashMap一样使用一个大数组的形式。但对某个元素进行put操作时,使用的是CAS操作,这样如果有多个线程操作这个位置的元素,同一时刻只有一个会成功。因此大多数情况下都是无锁操作,性能很高。只有对于有hash冲突而采用链表+红黑树进行处理的位置进行操作时,ConcurrentHashMap内部才需要对这个位置进行synchronized加锁处理。

到此这篇关于HashMap底层数据结构详细解析的文章就介绍到这了,更多相关HashMap数据结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 对Java中传值调用的理解分析

    对Java中传值调用的理解分析

    这篇文章主要介绍了对Java中传值调用的理解分析,通过分析对比,较为深入的分析了Java中传值调用的原理与用法,需要的朋友可以参考下
    2015-01-01
  • Mybatis中强大的resultMap功能介绍

    Mybatis中强大的resultMap功能介绍

    这篇文章主要给大家介绍了关于Mybatis中强大的resultMap功能的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • Spring Boot整合Elasticsearch实现全文搜索引擎案例解析

    Spring Boot整合Elasticsearch实现全文搜索引擎案例解析

    ElasticSearch作为基于Lucene的搜索服务器,既可以作为一个独立的服务部署,也可以签入Web应用中。SpringBoot作为Spring家族的全新框架,使得使用SpringBoot开发Spring应用变得非常简单,在本案例中我们给大家介绍Spring Boot整合Elasticsearch实现全文搜索引擎
    2017-11-11
  • Spring JPA之save方法示例详解

    Spring JPA之save方法示例详解

    这篇文章主要为大家介绍了Spring JPA之save方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Docker容器中的SSH免密登录详解

    Docker容器中的SSH免密登录详解

    这篇文章主要介绍了Docker容器中的SSH免密登录详解,在日常的开发和测试环境中经常需要创建和管理Docker容器,有时,出于调试或管理的目的,可能需要SSH到容器内部,本文将介绍如何创建一个Docker容器,它在启动时自动运行SSH服务,并支持免密登录,需要的朋友可以参考下
    2023-08-08
  • Java线程的生命周期和状态控制_动力节点Java学院整理

    Java线程的生命周期和状态控制_动力节点Java学院整理

    这篇文章主要介绍了Java线程的生命周期和状态控制,需要的朋友可以参考下
    2017-05-05
  • java实现一个接口调取另一个接口(接口一调取接口二)

    java实现一个接口调取另一个接口(接口一调取接口二)

    这篇文章主要介绍了java实现一个接口调取另一个接口(接口一调取接口二),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • SpringBoot SSMP 整合案例分享

    SpringBoot SSMP 整合案例分享

    这篇文章主要介绍了SpringBoot SSMP 整合案例分享,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • SpringBoot整合flyway实现自动创建表的方法

    SpringBoot整合flyway实现自动创建表的方法

    这篇文章主要介绍了SpringBoot整合flyway实现自动创建表的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • 简单分析Java的求值策略原理

    简单分析Java的求值策略原理

    在本篇文章里小编给大家整理的是一篇关于简单分析Java的求值策略原理内容,有需要的朋友们可以学习下。
    2021-06-06

最新评论