博客
关于我
HashMap源码解析
阅读量:798 次
发布时间:2019-03-25

本文共 2116 字,大约阅读时间需要 7 分钟。

HashMap深度探析:从底层到实际应用

HashMap作为Java编程中广泛使用的数据结构,常被用来在O(1)时间内完成查找和插入操作。然而,它的内部实现机制和设计原理却并不那么直观。本文将从 HashMap底层结构、扩容机制、链表与红黑树的转换,以及实际应用场景等多个方面,深入解析 HashMap的工作原理。


一、HashMap底层结构

HashMap的核心数据结构是一个数组,每个数组中的元素都是key-value对。在Java7及以前版本中,数组中的元素是称为Entry的对象;而在Java8及之后版本中,数组中的元素被称为Node。数组的每个位置存储的是一个key-value实例,在Java7中称为Entry,在Java8中称为Node。

数组的大小是动态扩展的。在插入操作时,根据键的哈希值计算出对应的数组位置,然后将指定的键-值对(Entry或Node)存储到该位置。当发生哈希冲突时(即多个键的哈希值映射到同一个数组位置),就需要使用链表来解决冲突。每个Node对象包含:-

  • 维度
  • 下一个指针
  • 节点的哈希值
  • 关键字

Java8之后的 HashMap实现进一步优化了冲突解决机制,引入了红黑树结构来取代链表,以减少在高负载情况下的查询时间。


二、链表的作用

哈希冲突是哈希表不能避免的问题。当多个键的哈希值相同,导致它们被存储到同一个数组位置时,就会形成链表。链表的作用是与哈希冲突中解决方案结合,确保快速查找和插入操作的效率。

每个Node对象都包含一个指向下一个Node的指针。新插入的Node会插入到链表的特定位置,取决于HashMap的链表插入策略。早期版本的Java(如Java7)使用的是头插法(NewKey)、而Java8及之后版本则改用尾插法(Tail-Insert)。尾插法的主要优点是减少,在多线程环境下可能导致链表顺序混乱的问题,从而提高了HashMap的稳定性和性能。


三、扩容机制

哈希表的容量决定了其性能。容量过低时,哈希冲突的概率较高,且查询效率下降。因此,HashMap会在需要的时候动态扩容,以保证其运算效率。

1. 容量和负载因子

HashMap的容量由两个因素决定:

  • 容量:表示数组中允许的最大键-值对数量。
  • 负载因子:默认值为0.75。当存储的键-值对数量超过容量乘以负载因子时,就会触发扩容操作。

例如,当前容量为100,负载因子为0.75,当存储的键-值对数量超过75,就会触发扩容。扩容后的容量是原来的两倍(即200),这样可以为新增存储提供更多空间。

2. 扩容步骤

当需要扩容时,HashMap会执行以下步骤:

  • 扩容:创建一个空的新Entry数组,其大小为原数组的两倍。
  • reHash:将原数组中的所有节点重新计算哈希值,并将它们重新存储到新数组中。
  • 这里需要注意的是,扩容过程中,原数组的节点会被重新哈希,这样就能保证新数组的节点分布更加均匀,减少哈希冲突的概率。同时,原数组的节点在扩容过程中会被清除,以节省内存空间。


    四、哈希值的计算

    哈希值的计算是整个HashMap的基础。每个键的哈希值通过以下公式计算:

    hash = key.toString().hashCode();

    对于数组的存储位置,使用的是以下公式:

    index = hash & (length - 1);

    其中,`length` 是数组的当前长度。这种哈希值计算方式避免了直接使用模运算(%),因为哈希值通过位运算(&)计算更高效,而位运算是对内存操作,速度更快。


    五、链表与红黑树的转换

    在实际应用中,为了保证哈希表的查询性能,链表长度会根据实际情况动态调整。Java8及之后版本的HashMap,将链表长度与红黑树之间进行了平衡。如果链表的长度超过等于8,就会将其转换为红黑树;而如果链表长度小于等于6,就会将其转换为一条链表。这种设计充分考虑了空间与时间的平衡关系,确保了HashMap在大多数场景下的高效性能。


    六、HashMap与其他Map的区别

    虽然HashMap和其他Map(如TreeMap、LinkedHashMap)有相似之处,但各自的设计目标和使用场景不同。以下是它们的主要区别:

  • HashMap

    • 关键字是随机分布的。
    • 查找速度极快,适合频繁的插入、删除和查找操作。
    • 没有保持顺序,遍历时结果不可预测。
  • TreeMap

    • 关键字按自然顺序存储。
    • 查找速度较慢,但输出结果是有序的。
    • 适用于需要遍历并且需要呈现有序结构的场景。
  • LinkedHashMap

    • 关键字按插入顺序存储。
    • 既支持高效的插入、删除和查找操作,又保留了插入顺序。
    • 适用于需要保持顺序的场景,例如记录日志或处理 kafka 消息。

  • 七、总结

    HashMap作为一种高性能的哈希表,广泛应用于Java程序开发中。理解其底层原理不仅能帮助我们更好地利用它,还能提升我们对数据结构设计的认识。无论在实际项目中还是面试中,HashMap的知识点都是不可或缺的。在深入理解它的同时,也建议学习其他经典数据结构(如TreeSet、PriorityQueue等)的实现和使用场景,以全面提升自己的编程能力。

    转载地址:http://tbsuk.baihongyu.com/

    你可能感兴趣的文章
    MySQL-数据页的结构
    查看>>
    MySQL-架构篇
    查看>>
    MySQL-索引的分类(聚簇索引、二级索引、联合索引)
    查看>>
    Mysql-触发器及创建触发器失败原因
    查看>>
    MySQL-连接
    查看>>
    mysql-递归查询(二)
    查看>>
    MySQL5.1安装
    查看>>
    mysql5.5和5.6版本间的坑
    查看>>
    mysql5.5最简安装教程
    查看>>
    mysql5.6 TIME,DATETIME,TIMESTAMP
    查看>>
    mysql5.6.21重置数据库的root密码
    查看>>
    Mysql5.6主从复制-基于binlog
    查看>>
    MySQL5.6忘记root密码(win平台)
    查看>>
    MySQL5.6的Linux安装shell脚本之二进制安装(一)
    查看>>
    MySQL5.6的zip包安装教程
    查看>>
    mysql5.7 for windows_MySQL 5.7 for Windows 解压缩版配置安装
    查看>>
    Webpack 基本环境搭建
    查看>>
    mysql5.7 安装版 表不能输入汉字解决方案
    查看>>
    MySQL5.7.18主从复制搭建(一主一从)
    查看>>
    MySQL5.7.19-win64安装启动
    查看>>