3分钟轻松理解单线程下的HashMap工作原理

简介: HashMap主要是用来处理键值对数据。随着JDK版本的更新,JDK1.8对HashMap对底层也做了一些优化。今天我带大家一起来结合源码,深入浅出HashMap工作原理。

HashMap主要是用来处理键值对数据。随着JDK版本的更新,JDK1.8对HashMap对底层也做了一些优化。今天我带大家一起来结合源码,深入浅出HashMap工作原理。

l

HashMap是基于哈希表对Map接口的实现类,它的特点是访问数据的速度快,并不是按顺序来遍历。

HashMap提供所有可选的映射操作,但不能保证映射顺序不变,并且允许使用空值和空键。HashMap也并不是线程安全的,当存在多个线程同时写入时,可能会导致数据不一致的情况。


【Java面试】三分钟轻松理解,单线程下的HashMap工作原理

1、HashMap中的关键属性

【导航条:关键属性】

要透彻理解HashMap原理,首先需要对以下几个关键属性有一个基本的认识。

029630d81b7641eda4885eb6fe7f6572.png

我们看到,HashMap的源码片段:

第一个属性 loadFactor,它是负载因子,默认值是0.75,表示扩容前 。

第二个属性 threshold 它是记录HashMap所能容纳的键值对的临界值,它的计算规则是负载因子 乘以 数组长度。

第三个属性 size,它用来记录HashMap实际存在的键值对的数量。

第四个属性 modCount,它用来记录HashMap内部结构发生变化的次数。

第五个是常量属性DEFAULT_INITIAL_CAPACITY ,它规定 的默认容量是16。

2、HashMap的存储结构

【导航条:存储结构】

931ecae2981b48719ce5c864fea5a9c3.png

HashMap采用的是 的存储结构。HashMap的数组部分称为Hash桶,数组元素保存在一个叫做table的属性中。当链表长度大于等于8时,链表数据将会以红黑树的形式进行存储,当长度降到6时,又会转成链表形式存储。

1f24ad78aa1e42c294f5cb8e6dcc13c6.png

每个Node节点,保存了用来定位数组索引位置的hash值、Key、Value和链表指向的下一个Node节点。而Node类是HashMap的内部类,它实现了Map.Entry接口,它的本质其实可以简单的理解成就是一个键值对。来看一下源码。

3、HashMap的工作原理

【导航条:工作原理】

当我们向HashMap中插入数据时,首先要确定Node在数组中的位置。那如何确定Node的存储位置呢?以添加Key为字符串“e”的对象为例:

3d9091f43ffc4ed4a21f12134436856b.png

8c6d243dd1c6400893d8a62f093fd0b7.png

HashMap首先调用hashCode()方法,获取Key的hashCode值为h。然后对h值进行高位运算;将h右移16位取得h的高16位,与 进行异或运算,最后得到h的值与 ( table.length - 1 )进行与运算获得该对象的保留位,最后计算出下标。当然,这是最官方的描述。有的小伙伴可能已经迷糊了。其实,这段运算过程,简单地理解成求模取余法。

就是用hash值和数组的长度减1,取模,最后得到数组的下标,这样可以保证数组下标不越界。只不过,位运算是二进制运算,效率更高。

最后,来看一段动画演示,假设有“a”、“b”、“d”、“r”,“t”,“e”的Key。通过计算得到的下标分别为 1 、2 、 4 、2 、4 、5

b5597ba7cc524867bad6a4ba166567e2.png

它们的插入顺序如动画所示。

9e55b689e0c845cc844475e2f52ff5c9.png

如果我们再次插入 “a”,“g”,“i”,null 四个Key,来看HashMap的内部变化。

42b80b3d0db64da584a6e13f20cc50ab.png

当插入第二个以a为Key的对象时,会将新值赋值给a的值。当插入的对象大小超过临界值时,HashMap将新建一个桶数组并重新赋值(当然,JDK1.7和1.8重新赋值的方式略有不同)

这个时候,HashMap键的输出顺序为 null、a、b、r、d、t、e、g、i

6bc9fec4bae1486e8c5f3bdeb91c74fe.png

HashMap的工作原理,你搞懂了吗?

S信【Tom】或【666】即可免费领取需要更多干货内容,还有海量面试资料,只弹干货不惨水!

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

相关文章
|
8天前
|
Java
并发编程之线程池的底层原理的详细解析
并发编程之线程池的底层原理的详细解析
45 0
|
24天前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。
|
2月前
|
算法 Unix 调度
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
33 0
|
2月前
|
算法 安全 调度
多线程如何工作,工作原理是什么
多线程如何工作,工作原理是什么
|
12天前
|
Java 调度
《Java 多线程实战系列》- 01 基本概念与底层原理
《Java 多线程实战系列》- 01 基本概念与底层原理
14 0
|
18天前
|
Java
Java线程通信的精髓:解析通知等待机制的工作原理
Java线程通信的精髓:解析通知等待机制的工作原理
26 3
Java线程通信的精髓:解析通知等待机制的工作原理
|
18天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
42 0
|
18天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(上)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)
35 0
|
2月前
|
存储 安全 Java
Java线程池ThreadPoolExcutor源码解读详解04-阻塞队列之PriorityBlockingQueue原理及扩容机制详解
1. **继承实现图关系**: - `PriorityBlockingQueue`实现了`BlockingQueue`接口,提供了线程安全的队列操作。 - 内部基于优先级堆(小顶堆或大顶堆)的数据结构实现,可以保证元素按照优先级顺序出队。 2. **底层数据存储结构**: - 默认容量是11,存储数据的数组会在需要时动态扩容。 - 数组长度总是2的幂,以满足堆的性质。 3. **构造器**: - 无参构造器创建一个默认容量的队列,元素需要实现`Comparable`接口。 - 指定容量构造器允许设置初始容量,但不指定排序规则。 - 可指定容量和比较
230 2
|
2月前
|
设计模式 安全 C++
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
238 2