likes
comments
collection
share

HashMap相关内容,面试专题

作者站长头像
站长
· 阅读数 16

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

HashMap相关内容,面试专题

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

HashMap相关内容,面试专题

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

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

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

数组特点:
存储区间是连续,且占用内存严重,空间复杂也很大,时间复杂为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的扩容机制及其原理

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

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

扩容的原理是:

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

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

扩容操作会在HashMap内部自动进行,开发者不需要手动触发。但需要注意的是,当HashMap中存储的键值对数量很大时,扩容操作可能会对性能产生影响。
因此,在实际使用中,可以适当调整初始容量和负载因子的大小,以平衡存储空间和查询性能的需求。