HashMap底层数据结构详细解析

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

Java技术迷

一、HashMap的底层数据结构

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

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

1
2
3
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的一段源码:

1
2
3
4
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数据结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://blog.csdn.net/zsh2050/article/details/127142851

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • RandomAccessFile简介_动力节点Java学院整理

    RandomAccessFile简介_动力节点Java学院整理

    RandomAccessFile 是随机访问文件(包括读/写)的类。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。这篇文章主要介绍了RandomAccessFile简介,需要的朋友可以参考下
    2017-05-05
  • Hibernate中Session增删改查操作代码详解

    Hibernate中Session增删改查操作代码详解

    这篇文章主要介绍了Hibernate中Session增删改查操作代码详解,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • SSM框架搭建图文教程(推荐)

    SSM框架搭建图文教程(推荐)

    下面小编就为大家带来一篇SSM框架搭建图文教程(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • Spring-Boot 集成Solr客户端的详细步骤

    Spring-Boot 集成Solr客户端的详细步骤

    本篇文章主要介绍了Spring-Boot 集成Solr客户端的详细步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Java实现解数独的小程序

    Java实现解数独的小程序

    最近在学习Java,然后上个月迷上了九宫格数独,玩了几天,觉得实在有趣,就想着能不能用编程来解决,于是就自己写了个,还真解决了。下面这篇文章就给大家主要介绍了Java实现解数独的小程序,需要的朋友可以参考借鉴。
    2017-01-01
  • Java Comparator比较器实例解析

    Java Comparator比较器实例解析

    这篇文章主要介绍了Java Comparator比较器实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • 通过实例解析Spring Ioc项目实现过程

    通过实例解析Spring Ioc项目实现过程

    这篇文章主要介绍了Spring Ioc项目实践过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • IDEA 如何导入别人的javaweb项目进行部署

    IDEA 如何导入别人的javaweb项目进行部署

    这篇文章主要介绍了IDEA 如何导入别人的javaweb项目进行部署,本文给大家分享我的详细部署过程及遇到问题解决方法,需要的朋友可以参考下
    2023-03-03
  • 详解Java方法method的定义与调用及重载

    详解Java方法method的定义与调用及重载

    方法,也称函数,如果想要重复一段或者多段代码块的使用,可以将这些代码封装成一个方法,方法具体表现为某种行为,使用方法可以提高代码的复用性
    2022-04-04
  • Gson之toJson和fromJson方法的具体使用

    Gson之toJson和fromJson方法的具体使用

    Gson是Google的一个开源项目,可以将Java对象转换成JSON,也可能将JSON转换成Java对象。本文就详细的介绍了toJson和fromJson方法的具体使用,感兴趣的可以了解一下
    2021-11-11

最新评论