作为一名程序员,你或许经常运用 HashMap 这个重要的数据结构,但你对它的底层完成原理或许不够了解。本文将经过图文结合的办法,为你详细解析 HashMap 的底层完成原理,并回答一些常见问题,让你能够更好地了解和运用 HashMap。

1. HashMap 概述

HashMap 是 Java 集合结构中最常用的映射表完成,它提供了键值对的存储和检索功用。底层依据数组和链表(或红黑树)完成,经过哈希算法将键映射到数组的索引方位,以完成快速的刺进和查找操作。下面咱们来看一下 HashMap 的底层代码流程图:

详解 HashMap 的底层实现原理

2. HashMap 的主要办法剖析

2.1put办法

put办法用于将键值对刺进到 HashMap 中。让咱们看一下put办法的源代码:

详解 HashMap 的底层实现原理

首要,经过has(key)办法核算键的哈希码,并调用putVal()办法进行刺进操作。下面是putVal()办法的源代码:

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

putVal()办法的中心是经过哈希码定位桶,然后在桶中进行刺进操作。假如桶为空,则直接在桶中刺进新节点。假如桶不为空,则遍历链表或红黑树,查找键是否已存在。假如键已存在,则替换对应的值;假如键不存在,则将新节点刺进到链表的结尾,并依据链表长度是否到达阈值来决议是否将链表转化为红黑树。最终,更新修正次数和元素数量,并进行必要的操作。

2.2 resize办法

resize办法用于动态扩容 HashMap。当元素数量超过阈值时,HashMap 会主动触发扩容操作。下面是resize办法的源代码:

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

resize办法的主要功用是创立一个新的数组,并将一切键值对从头核算哈希码和索引方位,然后将它们分配到新的桶中。扩容后的容量是本来的两倍,并依据负载因子从头核算阈值。在搬运键值对时,会依据哈希码的高位来确认是保留在本来的方位仍是移动到新的方位。假如链表长度超过阈值,则会将链表转化为红黑树以进步查找功率。

2.3 get办法

当咱们需求依据键来获取值时,能够运用 HashMap 的get(key)办法。下面讲解下 HashMap 的get办法的原理。

首要,咱们传入要查找的键key,然后经过hash(key)办法核算键的哈希码。哈希码被用来确认键在 HashMap 中的桶(bucket)方位。依据哈希码,咱们能够确认键在数组中的索引方位。

接下来,咱们在确认的索引方位找到对应的桶。假如桶为空,即没有键值对存在,那么回来 null,表示没有找到对应的值。

假如桶不为空,那么或许存在多个键值对,这时咱们需求遍历链表或红黑树来找到具有相同键的节点。在遍历过程中,咱们会运用键的 equals() 办法来比较传入的键和当时节点的键是否持平。假如找到了持平的键,那么回来对应节点的值。

假如遍历完整个链表或红黑树依然没有找到持平的键,那么回来 null,表示没有找到对应的值。

整个get()办法的原理便是依据键的哈希码确认索引方位,然后在对应的桶中遍历链表或红黑树,经过 equals() 办法比较键的持平性来找到对应的值。

以下是get办法的伪代码示例:

详解 HashMap 的底层实现原理

详解 HashMap 的底层实现原理

经过剖析上述代码,咱们能够看到get办法的完成流程:首要核算键的哈希码,然后依据哈希码找到对应的桶,最终在桶中遍历链表或红黑树,查找具有相同键的节点,假如找到则回来对应的值,否则回来 null。

相关内容拓宽:(技术前沿)

近10年间,甚至连传统企业都开始大面积数字化时,咱们发现开发内部工具的过程中,很多的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的很多时刻。

针对这类问题,低代码把某些重复出现的场景、流程,具象化成一个个组件、api、数据库接口,避免了重复造轮子。极大的进步了程序员的生产功率。

引荐一款程序员都应该知道的软件JNPF快速开发渠道,选用业内抢先的SpringBoot微服务架构、支撑SpringCloud模式,完善了渠道的扩增根底,满足了体系快速开发、灵活拓宽、无缝集成和高功能运用等归纳才能;选用前后端分离模式,前端和后端的开发人员可分工合作负责不同板块,省劲又快捷。

免费体会官网:www.jnpfsoft.com/?juejin ,还没有了解低代码这项技术能够赶忙体会学习!

3. 常见问题剖析

3.1为什么哈希码很重要?

哈希码在 HashMap 中扮演着重要的人物。它经过哈希函数将键转换为一个仅有的整数,决议了键值对在数组中的存储方位。假如两个键的哈希码不同,它们会被分配到不同的桶中,进步了查找功率。假如哈希码相同,就会发生哈希抵触,需求进一步处理。

3.2怎么处理哈希抵触?

当两个不同的键具有相同的哈希码时,HashMap 会运用链表或红黑树来解决哈希抵触。链表是 JDK 8 之前的解决方案,而红黑树是 JDK 8 之后的优化。HashMap 在桶中经过链表或红黑树结构存储抵触的键值对,以便在查找时能快速定位到正确的键值对。

3.3为什么需求动态扩容?

动态扩容是为了避免哈希抵触过多,进步 HashMap 的功能。当键值对的数量接近数组容量的阈值时,HashMap 会主动触发扩容操作。它创立一个更大的数组,并从头核算每个键的哈希码和索引方位,然后将键值对从头刺进到新数组中。这样能够削减桶的数量,降低哈希抵触的概率,进步存储和检索的功率。

3.4怎么保证键的仅有性?

HashMap 经过哈希码和链表/红黑树结构来保证键的仅有性。当存储键值对时,假如发现相同的键已经存在于桶中,HashMap 会检查键的 equals() 办法来确认是否是同一个键。假如 equals() 办法回来 true,新的键值对会替换旧的键值对;假如 equals() 办法回来 false,新的键值对会被添加到桶中。这样就保证了 HashMap 中的键是仅有的。

3.5HashMap 和线程安全有关吗?

HashMap 在默认情况下对错线程安全的。多个线程一起对 HashMap 进行刺进、删去或查找操作或许会导致不一致的结果。假如在并发环境下运用 HashMap,应考虑运用线程安全的 ConcurrentHashMap 或运用适当的同步机制来维护 HashMap 的拜访。

3.6怎么挑选适当的初始容量和负载因子?

HashMap 的初始容量和负载因子会影响其功能和空间利用率。初始容量是指 HashMap 初始化时的桶数量,默认为 16。负载因子是指 HashMap 在扩容之前允许的均匀桶占用比例,默认为 0.75。

挑选适当的初始容量和负载因子取决于你的运用需求。假如估计存储的键值对数量较多,能够挑选一个较大的初始容量,以削减动态扩容的频率。负载因子较小能够削减哈希抵触的概率,但会添加空间占用。归纳考虑,一般能够运用 HashMap 的默认值,并依据实际情况进行调整。

HashMap 是一个强大而灵活的数据结构,合理运用它能够进步程序的功能和功率。经过深化了解 HashMap 的底层完成原理,你能够更好地了解其工作办法,并在实际开发中做出更明智的设计和优化决议计划。

结论

经过以上的源代码剖析和常见问题的解答,相信你已经对 HashMap 的底层完成原理有了更深化的了解。HashMap 的底层运用数组和链表(或红黑树)完成,经过哈希算法将键映射到数组的索引方位,以完成快速的刺进和查找操作。动态扩容过程会创立一个更大的数组,并从头分配键值对到新的桶中,以进步功能。一起,咱们还回答了一些常见问题,希望能协助你更好地了解和运用 HashMap。

作者介绍

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。