前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文讲懂HashMap

一文讲懂HashMap

原创
作者头像
疯狂的KK
修改于 2023-07-04 01:46:17
修改于 2023-07-04 01:46:17
8100
举报
文章被收录于专栏:Java项目实战Java项目实战

HashMap 面试题解析

HashMap 是 Java 中非常重要的类,在面试中经常被提及。本文将通过介绍 HashMap 基本原理以及经典面试问题进行分析。

工作原理

HashMap 属于 Map 接口的一种实现,其基本实现原理是拉链法

其内部主要包含了两个组成部分:数组table 和 桶(链表)bucket。

当对 HashMap 放入一个<key,value> 键值对时,会先对 key 调用 hashCode() 方法计算出一个哈希值,再通过一种散列函数将哈希值映射到 table 数组中的一个位置 index,随后将<key,value> 添加到 index 处的 bucket 中。

如对于 key1 来说:

  1. int hash = key1.hashCode();
  2. int index = hash & (table.length - 1);
  3. 将<key1, value1>添加到tableindex链表上。 当使用 get() 方法获取键值对时,也会先计算 index,再从对应的链表中找寻键的具体位置。容量相关
    1. 容量大小:HashMap 的容量为一个桶数组 table 的长度,table 的初始大小为 16,并且都是 2 的幂次方。
    1. 容量变化:当存放元素超过负载因子(默认 0.75)时,HashMap 会进行 resize 操作,扩大桶数组 table 的容量。
    1. 扩容步骤: 1) 创建一个容量为旧容量两倍的新桶数组 2) 遍历旧桶数组中的每个元素,重新计算 index,并放入新桶数组,这一步需要较多时间。 3) 将旧桶数组指向新桶数组。碰撞问题冲突(Collision) 是 HashMap 中的一个重要问题。我们知道相同 key 会映射到同一个 index,造成链表的多条记录。
    1. 开放地址法:链地址法。新元素不断找下一个空的位置插入。
    1. 拉链法:新元素直接加入链表尾部,HashMap 采用的就是这种方法。
    1. 再哈希法:重新计算 hash 值,再得到一个不同的 index。 解决冲突有利于提高 HashMap 中搜索的效率。1. HashMap 的基本原理HashMap 的核心原理是哈希函数,它通过一个哈希函数将键映射到一个索引位置,然后在该索引位置上存储对应的值。哈希函数的设计需要满足均匀分布,以确保哈希冲突的概率最小。HashMap 中使用了一种叫做“开放地址”的策略来解决哈希冲突,即当两个键映射到同一个位置时,不直接覆盖原有的值,而是通过链表、红黑树等数据结构将这两个值存储在一起。2. HashMap 的存储结构HashMap 的存储结构包括两部分:哈希表和链表/红黑树。哈希表是一部分,它存储了所有的键值对,每个键值对都由一个哈希值和一个指向链表或红黑树的指针组成。链表或红黑树是另一部分,它们用于存储具有相同哈希值的键值对。当哈希冲突发生时,HashMap 会根据哈希冲突的位置将键值对插入到链表或红黑树中。3. HashMap 的插入、查找、删除操作HashMap 的插入操作分为两个步骤:计算哈希值和插入键值对。计算哈希值的目的是确定键值对在哈希表中的存储位置,这一步可以通过哈希函数来完成。插入键值对的过程分为两种情况:
  4. 当哈希值对应的位置为空时,直接将键值对插入到该位置。
  5. 当哈希值对应的位置不为空时,需要遍历链表或红黑树,查找是否存在相同的键值对。如果不存在,则插入键值对;如果存在,则根据键值对的比较结果进行更新。 HashMap 的查找操作也是基于哈希函数的,它首先计算键的哈希值,然后根据哈希值在哈希表中查找对应的键值对。如果找到了,则直接返回对应的值;否则,返回 null。 HashMap 的删除操作与插入操作类似,也需要遍历链表或红黑树。在遍历过程中,需要根据键值对的比较结果进行更新,以保持链表或红黑树的有序性。

4. HashMap 的并发访问问题

HashMap 在多线程并发访问时,可能会导致数据不一致或死循环等问题。这是因为 HashMap 的插入、查找、删除操作都需要遍历链表或红黑树,而遍历过程是一个线性的过程,无法并行执行。因此,在多线程环境下,需要对 HashMap 进行同步,以确保数据的安全和一致性。

5. HashMap 的泛型参数

HashMap 有一个泛型参数,用于指定键和值的类型。这个泛型参数可以是任何类型,包括基本类型、引用类型和数组类型等。在使用 HashMap 时,需要指定键和值的类型,并且键的类型不能为 null。

6. HashMap 与 TreeMap 的比较

HashMap 和 TreeMap 都是 Java 中常用的映射类型,它们之间有几个重要的区别:

  • 存储结构:HashMap 使用哈希表和链表/红黑树存储数据,而 TreeMap 使用二叉树存储数据。
  • 访问性能:由于 HashMap 使用了哈希函数,因此它的访问速度更快,尤其是针对特定的键值对。TreeMap 的访问性能则依赖于二叉树的高度。
  • 插入、删除操作:HashMap 的插入、删除操作比较快,因为它们只需要修改链表或红黑树。TreeMap 的插入、删除操作需要修改整个二叉树,因此性能相对较差。
  • 空间需求:HashMap 的空间需求与键值对的数量有关,而 TreeMap 的空间需求与二叉树的高度有关。因此,在键值对数量较小的情况下,HashMap 的空间需求更小;而在键值对数量较大的情况下,TreeMap 的空间需求更小。HashMap: 实现原理及优化

1. HashMap的数据结构

HashMap是一种以键值对(key-value)形式存储数据的数据结构,它基于哈希表的实现。其中,键(key)用于唯一标识元素,值(value)则是与键相关联的数据。在HashMap中,键是唯一的,而值可以重复。

2. HashMap的工作原理

HashMap通过将键的哈希值映射到一个数组的索引位置来存储和获取数据。具体来说,当将一个键值对放入HashMap时,首先会计算键的哈希值,并根据哈希值找到对应的索引位置。如果该位置还没有元素,就直接将键值对存储在该位置上;如果该位置已经有元素,就使用链表或红黑树等数据结构将新的键值对追加到该位置上,以解决哈希冲突问题。

3. 当两个对象的hashCode相同会发生什么?

当两个不同的对象的hashCode相同时,会产生哈希冲突。这意味着这两个对象在HashMap中可能会被分配到相同的索引位置上。为了解决这个问题,HashMap使用链表或红黑树等数据结构将发生哈希冲突的元素链接在一起。

4. hash的实现及其原因

hash是将任意长度的输入通过哈希函数转换为固定长度的输出的过程。在HashMap中,哈希函数(Hash Function)负责计算键的哈希值。一个好的哈希函数应具备以下特点:

  • 快速计算。哈希函数应该能够在常数时间(O(1))内计算出哈希值,以保证高效的插入、查找和删除操作。
  • 均匀分布。哈希函数应该将键的各种组合均匀地映射到哈希表的各个位置,以尽量减少哈希冲突。
  • 随机性。哈希函数应该在一定程度上随机化,以防止恶意攻击者构造特定的输入来导致大量哈希冲突,并影响HashMap的性能。

5. HashMap的容量确定及loadFactor

HashMap的容量由table数组的长度决定,一般为2的幂次方。loadFactor(负载因子)是一个比例,默认为0.75。当HashMap中已存储的元素数量超过loadFactor乘以容量时(即负载因子阈值),就会触发数组的扩容操作。

容量的变化涉及两个相关的指标:扩容阈值(threshold)和加载因子(loadFactor)。扩容阈值等于容量乘以加载因子。当元素数量超过扩容阈值时,HashMap会进行扩容,将容量翻倍,然后重新计算扩容阈值。

6. HashMap中put方法的过程

当调用HashMap的put方法时,它会按照以下步骤进行操作:

  1. 根据键的哈希值计算出对应的数组索引。
  2. 如果该索引位置上没有元素,则直接将键值对存储在该位置上。
  3. 如果该索引位置上已有元素,则使用链表或红黑树等数据结构追加到该位置上。
  4. 如果追加的元素个数达到一定阈值(一般为8),并且HashMap中的总元素数量超过扩容阈值,就会触发数组的扩容操作。
  5. 如果添加的键已存在于HashMap中,则新的值会覆盖旧的值。

7. 数组扩容的过程

数组的扩容是为了解决哈希冲突和提高HashMap的性能。当HashMap中的元素数量超过扩容阈值时,会触发数组的扩容操作。扩容过程分为以下几个步骤:

  1. 创建一个新的数组,长度是原数组长度的两倍。
  2. 将原数组中的元素逐个重新计算哈希值,并根据新的数组长度找到对应的位置。
  3. 将元素按照新的索引位置重新插入新的数组中。
  4. 扩容完成后,HashMap中的table引用指向新的数组。

8. 红黑树与链表

在HashMap中,当哈希冲突较严重时,链表的长度可能会变得很长,这会导致查找的时间复杂度从O(1)变为O(n),严重影响性能。为了解决这个问题,Java 8引入了红黑树来替代链表。

红黑树是一种自平衡二叉查找树,它的插入、删除和查找操作的平均时间复杂度为O(log n)。当链表长度超过一个阈值(默认为8)时,HashMap会将链表转换为红黑树。当红黑树的节点数量减少到一定程度(阈值为6),又会将红黑树转换回链表。

选择红黑树而不是二叉查找树的原因在于红黑树具有更好的平衡性,能够保证最坏情况下的性能。而二叉查找树在某些情况下可能会退化,导致查找操作的时间复杂度为O(n)。

9. 对红黑树的见解

红黑树是一种自平衡的二叉查找树,它在插入、删除和查找操作上具有良好的平均和最坏情况性能。以下是对红黑树的一些见解:

  • 红黑树的高度是不超过2log(n+1)的,其中n是树中节点的数量。这保证了红黑树的操作的时间复杂度为O(log n)。
  • 红黑树的自平衡性是通过四个主要性质来实现的:树的节点是红色或黑色的,根始终是黑色的,每个叶节点(NIL节点)是黑色的,如果一个节点是红色的,则它的两个子节点都是黑色的,不能有两个连续的红色节点。
  • 红黑树的旋转操作用于保持树的平衡性,包括左旋和右旋。通过旋转,可以将红黑树的节点重新调整,使之满足红黑树的性质。
  • 红黑树在很多高级数据结构和算法中都有应用,如平衡二叉查找树、区间树等。

10. jdk8中对HashMap的改变

在JDK 8中,Java对HashMap做了一些改变,主要包括以下两个方面:

  1. 引入红黑树。为了解决在哈希冲突严重时,链表长度过长导致性能下降的问题,将链表转换为红黑树,提高了查找的效率。
  2. 对哈希算法的优化。在JDK 8中,对哈希函数的计算进行了改进,使得哈希值更加均匀分布,减少了哈希冲突的概率。

这些改变使得HashMap在处理大量数据时具有更好的性能和可扩展性。同时,在实际应用中,我们也可以根据需求选择使用其他具有类似功能的数据结构,如ConcurrentHashMap等。

结论

HashMap作为一个常用的数据结构,在实际应用中扮演着重要角色。选择合适的哈希函数实现、优化扩容策略、使用红黑树解决哈希冲突等都是为了提高HashMap的性能和可靠性。

通过深入理解HashMap的工作原理和优化策略,我们可以更好地使用HashMap,并在需要的时候根据实际需求选择合适的数据结构和算法,以获得更好的性能和效果。

其他问题

  • HashMap 不是线程安全的,在多线程中需要进行同步或者使用 ConcurrentHashMap。
  • HashMap 允许是 key 为 null,但只有一个 null key。
  • 不保证元素的顺序,可以使用 LinkedHashMap 来保持元素的插入顺序。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java集合面试题&知识点总结(下篇)
解答:Map 是 Java 集合框架中的一个接口,它存储键值对(key-value)的数据结构。
栗筝i
2023/11/02
2440
【Java编程进阶之路 02】深入探索:红黑树如何重塑哈希表的性能边界
Java中的HashMap是一种非常常用的数据结构,它以键-值对的形式存储数据,并能快速地进行数据的查找、插入和删除操作。在JDK1.8以后,HashMap的内部结构发生了一些重要的变化,其中最显著的变化是引入了红黑树来处理哈希冲突,以提高查询性能。本文将详细描述这些变化,并提供相关的源码片段进行解析。
夏之以寒
2024/03/05
1880
HashMap&ConcurrentHashMap&HashTable
JDK1.8以前Hashmap底层是数组和链表结合在一起使用,也就是散列链表。hashmap的key的hashcode()扰动函数处理后得到hash值,然后通过(n-1)& hash 判断当前元素存放的位置,如果当前位置存在元素的话,就判断当前位置存在的元素是否与之相同,相同则直接覆盖,不相同就通过拉链法解决冲突。
Tim在路上
2020/08/04
4330
深度解析HashMap:探秘Java中的键值存储魔法
https://cloud.tencent.com/developer/article/2465647?shareByChannel=link
忆愿
2024/11/19
2410
深度解析HashMap:探秘Java中的键值存储魔法
阿里 HashMap 面试夺命连环 21 问
A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过 8 时,链表转换为红黑树。
程序员追风
2022/03/04
6800
阿里 HashMap 面试夺命连环 21 问
这21个刁钻的HashMap面试题,我把阿里面试官吊打了
A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过 8 时,链表转换为红黑树。
一个会写诗的程序员
2020/05/18
2.4K0
HashMap常见面试题_java面试题大汇总
大家好,又见面了,我是你们的朋友全栈君。 目录 1.HashMap的数据结构? 2.HashMap的工作原理? 3.当两个对象的hashCode相同会发生什么? 4.你知道hash的实现吗?为什么要这
全栈程序员站长
2022/09/22
3920
HashMap常见面试题_java面试题大汇总
深入理解HashMap:Java中的键值对存储利器
HashMap是Java中常用的数据结构之一,它提供了一种键值对的存储机制,适用于快速查找和检索。本文将深入探讨HashMap的概念、内部结构、工作原理以及在多线程环境下的一些问题。
人不走空
2024/02/20
3350
内含扩容源码的面试题,目标是手写HashMap!
    推荐在单线程环境下使用HashMap替代,如果需要多线程使用则用ConcurrentHashMap。
上分如喝水
2021/08/16
3960
内含扩容源码的面试题,目标是手写HashMap!
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
小马哥学JAVA
2024/12/17
2030
2024年java面试准备--集合篇
Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
终有救赎
2023/10/16
4520
2024年java面试准备--集合篇
Java之HashMap详解
在项目开发中,HashMap是及其常用的数据结构。今天,我们一起来看看它的源码实现(本文源码来自JDK 1.8)。
阿珍
2024/11/13
950
Java之HashMap详解
一文带你网罗HashMap面试考点!
达摩:其实我也不是很记得了(请继续装),但我还是记得那么一些,如果你是面的JAVA,首先当然是
阿泽
2019/09/24
1.1K0
一文带你网罗HashMap面试考点!
HashMap和TreeMap的内部结构
1、基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
哲洛不闹
2019/03/12
6280
HashMap和TreeMap的内部结构
彻底理解HashMap及LinkedHashMap
blog.csdn.net/fuzhongmin05/article/details/104355841
Java小咖秀
2021/07/12
1.3K0
JAVA面试备战(二)--集合
List(对付顺序的好帮手):List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
程序员爱酸奶
2022/04/12
5190
JAVA面试备战(二)--集合
HashMap连环18问
对于 JAVA 求职者来说,HashMap 可谓是重中之重,是面试必考点。然而 HashMap 的知识点非常多,复习起来花费精力很大,库森当年校招面试时就经历过这种痛苦。所以,结合自己的面试经验,斗胆写一篇关于 HashMap 的面试专题文章,希望对小伙伴有所帮助!
狼王编程
2022/09/22
5970
HashMap连环18问
面渣逆袭:HashMap追魂二十三问
HashMap作为我们熟悉的一种集合,可以说是面试必考题。简单的使用,再到原理、数据结构,还可以延伸到并发,可以说,就一个HashMap,能聊半个小时。
三分恶
2021/12/04
4350
面渣逆袭:HashMap追魂二十三问
Java 中 HashMap 数据结构分析(语言无关)
二叉搜索树的特性便于我们进行查找插入删除等一系列操作,其时间复杂度为O(logn),但是,如果遇见最差的情况,比如以下这棵树:
wsuo
2020/07/31
7290
Java 中 HashMap 数据结构分析(语言无关)
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
十一、为什么我们需要hash()函数 (n-1)\&hash,而不是直接用key的hashcode直接计算下标
寻求出路的程序媛
2024/10/17
8060
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
推荐阅读
相关推荐
Java集合面试题&知识点总结(下篇)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档