HashMap相关内容,面试专题

HashMa是Java中最常用的集合类框架,也是Java语言中非常典型的数据结构,同时也是我们需要掌握的数据结构,更重要的是进大厂面试必问之一。如下图: 集合类分为两大类,从Object这个顶级父类开始的话,Map就是所有map类集合的父类,但对于List与Set,它俩有个共同的父类collection,实现了 Iterable 接口。而HashMap继承的是一个抽象的 AbstractMap类,其实现了Map接口

HashMap的底层其实是由数组与链表或者红黑树组成的,红黑树与链表不会同时存在,只有当链表的长度达到默认的链表长度阈值(8)以及同一桶(数组索引)下的数据长度(此时还是链表)达到8的时,会由链表转换为红黑树,yJDK8之后,如果哈希表单向链表中元素超过8个,那么单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6个,会重新把红黑树变成单向链表数据结构。。大抵是这样存在着,如下图

至于为什么HashMap明明是一种集合类,底层却是数组?

复制代码
这是由于HashMap是一种基于哈希表实现的数据结构。哈希表需要将键通过哈希函数转换为一个索引,然后将值存储在对应的索引位置上。
而数组正好可以满足这个需求,可以通过索引直接访问对应位置的元素,使得哈希表的查找效率非常高。
因此,HashMap使用数组来存储键值对,其中数组的每个元素都是一个链表(或红黑树),用于解决哈希冲突。

HashMap链表的存在主要是为了解决hash冲突的问题,其次链表的存在是在数据不是很多的时候可以更快速的完成插入,其查询效率不也会太差。 只有当链表的数据长度达到8的时候,此时链表的查询效率就很差了,就会转换为红黑树的数据结构形式存在。

makefile 复制代码
数组特点:
存储区间是连续,且占用内存严重,空间复杂也很大,时间复杂为O(1)。
优点:是随机读取效率很高,原因数组是连续(随机访问性强,查找速度快)。
缺点:插入和删除数据效率低,因插入数据,这个位置后面的数据在内存中要往后移的,且大小固定不易动态扩展。

链表特点:
区间离散,占用内存宽松,空间复杂度小,时间复杂度O(N)。
优点:插入删除速度快,内存利用率高,没有大小固定,扩展灵活。
缺点:不能随机查找,每次都是从第一个开始遍历(查询效率低)。

1、map.put(k,v)实现原理

第一步首先将key,value封装到Node(节点)对象当中。第二步它的底层会调用K的HashCode()方法得出Hash值。第三步通过哈希表函数/哈希算法,将Hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。

2、map.get(k)实现原理

第一步:先调用key的HashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。重点理解如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,相当于遍历这个链表,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。

HashMap的扩容机制及其原理

markdown 复制代码
HashMap的扩容机制是为了重新分配更大的内部数组,以提供更多的桶位来存储键值对,从而保持哈希冲突的概率较低,提高HashMap的性能。

当HashMap的元素个数达到负载因子(默认为0.75)乘以当前容量时,会触发自动扩容操作。负载因子是表示内部数组多满时触发扩容的阈值。
例如,如果初始容量为16,那么当元素个数超过12(16 * 0.75)时,HashMap会自动扩容。

扩容的原理是:

1.  创建一个新的内部数组,大小为原数组的两倍。
2.  遍历原数组中的每个桶位,将其中的键值对重新分配到新的数组对应的桶位上。
3.  如果同一个桶位中存在多个键值对(通过链表或红黑树存储),则会保持它们在新数组中的相对顺序。

在扩容过程中,重新分配键值对的操作需要重新计算键的哈希值,并根据新数组的大小重新计算桶位的索引位置。
这个过程可能会比较耗时,但由于新的数组大小增大了两倍,因此扩容后的HashMap会有更多的桶位来存储键值对,从而减少了哈希冲突的概率。

扩容操作会在HashMap内部自动进行,开发者不需要手动触发。但需要注意的是,当HashMap中存储的键值对数量很大时,扩容操作可能会对性能产生影响。
因此,在实际使用中,可以适当调整初始容量和负载因子的大小,以平衡存储空间和查询性能的需求。
相关推荐
n8n12 小时前
RabbitMQ全面详解:从核心概念到企业级应用
java·rocketmq
用户7851278147012 小时前
实战代码:获取淘宝商品详情数据接口
java
我是天龙_绍12 小时前
用SpringMvc,实现,增删改查,api接口
后端
Chan1612 小时前
流量安全优化:基于 Sentinel 实现网站流量控制和熔断
java·spring boot·安全·sentinel·intellij-idea·进程
小蜗牛编程实录13 小时前
MAT分析内存溢出- ShardingSphere JDBC的缓存泄露问题
后端
用户685453759776913 小时前
🚀 Transformer:让AI变聪明的"读心术大师" | 从小白到入门的爆笑之旅
人工智能·后端
深圳蔓延科技13 小时前
SpringSecurity中如何接入单点登录
后端
刻意思考13 小时前
服务端和客户端之间接口耗时的差别
后端·程序员
该用户已不存在13 小时前
Python项目的5种枚举骚操作
后端·python
源码7可13 小时前
Java高手速成--吃透源码+手写组件+定制开发
java