HashMap常见面试题

1、HashMap的node

HashMap类有一个非常重要的属性Node<K,V>是HashMap的一个内部类,实现了Entry接口。本质上是一个映射。如下图:

static class Node<K, V> implements Map.Entry<K, V> {
        final int hash; //key的hash值
        final K key; //与你put进去的key一样
        V value; //与你put进去的value一样
        Node<K, V> next;//存放下一个节点
}

2、HashMap里面的返回值为什么不是key.hashCode()的返回值,而是key.hashCode()^(key.hashCode()>>>16)的返回值呢?

这样做的目的是为了减少hash的冲突概率,计算出hash值h只会,对h和h无符号右移16位做异或运算。实质上就是将底的16为与高的16位异或运算,这样参加计算后,能够减少hash冲突。

为什么要设计成高低16位异或呢?

因为 key.hashCode()函数调用的是 key 键值类型自带的哈希函数,返回 int 型散列值。int 值范围为很大,前后加起来大概 40 亿的映射空间。只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个 40 亿长度的数组,内存是放不下的。你想,如果 HashMap 数组的初始大小才 16,用之前需要对数组的长度取模运算,得到的余数才能用来访问数组下标。异或运算还比取模快。

结果显示,当 HashMap 数组长度为 512 的时候(2929),也就是用掩码取低 9 位的时候,在没有扰动函数的情况下,发生了 103 次碰撞,接近 30%。而在使用了扰动函数之后只有 92 次碰撞。碰撞减少了将近 10%。看来扰动函数确实还是有功效的。

3、为什么HashMap要用数组+链表+红黑树实现?

使用数组的原因是因为数组获得元素的时间复杂度为o(1),链表是为了当发生hash冲突时,可以继续存放元素,红黑树是为了在极端情况下二叉树的不平衡。当数组长度大于64且链表长度大于8的时候,链表才会转红黑树。链表长度大于8,数据查找效率变慢,还有一个原因是链表长度大于8发生的概率很小。一开始不适用红黑树是因为红黑树左旋,右旋维护成本太高。

根据jdk代码能够发现,链表长度(也是发生hash碰撞的概率)概率如下图: 

4、HashMap的put方法的大致实现流程

5、HashMap中数组大小为什么要是2的幂次方

因为只有当n为2的幂次方时,才满足hash%n=(n-1)&hash。按位&运算要比%取模快很多倍,十几倍。能保证索引值在范围中,不会超过数组长度。就算你给个长度为11,hashmap内部也会强制转换成你跟你给的值最接近,而且满足等于2的n次幂。

6、HashMap如何计算下标的=

 HashMap中数组下标值的计算过程,大致分为如下几步:获取key.hashCode(),然后将hashCode高16位和低16位异或(^)操作,然后与当前数组长度-1结果进行与(&)操作,最终结果就是数组的下标值。
至于由于当前结点(键值对)的加入,导致当前HashMap中容量超过了阈值而扩容2倍,扩容后导致每个结点重新计算下标值(称为新下标值),新下标值只有两种可能:一、key的hash()返回值新加入与(&)操作计算的一位为0,则下标值与原下标值相同。二、key的hash()返回值新加入与(&)操作计算的一位为1,则计算出来的下标值=原下标值+原数组长度。如果key对象为Integer,他的hashCode()直接返回的value。

7、JDK1.8下的HashMap是如何解决线程安全问题的?

在多线程环境下,1.7 会产生死循环、数据丢失、数据覆盖的问题,1.8 中会有数据覆盖的问题,以 1.8 为例,当 A 线程判断 index 位置为空后正好挂起,B 线程开始往 index 位置的写入节点数据,这时 A 线程恢复现场,执行赋值操作,就把 A 线程的数据给覆盖了;还有++size 这个地方也会造成多线程同时扩容等问题。

通过扰动函数加高低位异或。

8、Java 中有 HashTable、Collections.synchronizedMap、以及 ConcurrentHashMap 可以实现线程安全的 Map。

HashTable 是直接在操作方法上加 synchronized 关键字,锁住整个数组,粒度比较大,Collections.synchronizedMap 是使用 Collections 集合工具的内部类,通过传入 Map 封装出一个 SynchronizedMap 对象,内部定义了一个对象锁,方法内通过对象锁实现;ConcurrentHashMap 使用分段锁,降低了锁粒度,让并发度大大提高。

9、那你知道 ConcurrentHashMap 的分段锁的实现原理吗?

ConcurrentHashMap 成员变量使用 volatile 修饰,免除了指令重排序,同时保证内存可见性,另外使用 CAS 操作和 synchronized 结合实现赋值操作,多线程操作只会锁住当前操作索引的节点。

如下图,线程 A 锁住 A 节点所在链表,线程 B 锁住 B 节点所在链表,操作互不干涉。

10、你前面提到链表转红黑树是链表长度达到阈值,这个阈值是多少?

阈值是 8,红黑树转链表阈值为 6

11、为什么是 8,不是 16,32 甚至是 7 ?又为什么红黑树转链表的阈值是 6,不是 8 了呢?

因为经过计算,在 hash 函数设计合理的情况下,发生 hash 碰撞 8 次的几率为百万分之 6,概率说话。。因为 8 够用了,至于为什么转回来是 6,因为如果 hash 碰撞次数在 8 附近徘徊,会一直发生链表和红黑树的转化,为了预防这种情况的发生。

12、有序的map

LinkedHashMap 内部维护了一个单链表,有头尾节点,同时 LinkedHashMap 节点 Entry 内部除了继承 HashMap 的 Node 属性,还有 before 和 after 用于标识前置节点和后置节点。可以实现按插入的顺序或访问顺序排序。

13、LinkedHashMap 和 TreeMap

TreeMap 是按照 Key 的自然顺序或者 Comprator 的顺序进行排序,内部是通过红黑树来实现。所以要么 key 所属的类实现 Comparable 接口,或者自定义一个实现了 Comparator 接口的比较器,传给 TreeMap 用户 key 的比较。

14、JDK1.7的HashMap的rehash底层如何实现的?

15、HashMap中的modcount表示什么意思?

部分内容参考了牧云君博客文章

hashmap头插法和尾插法区别_一个跟面试官扯皮半个小时的HashMap_牧云君的博客-CSDN博客

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap面试题主要涉及以下几个方面: 1. HashMap的底层数据结构是什么? HashMap的底层数据结构是数组。当进行put()操作时,会进行hash计算,确定对象在数组中的位置。如果多个对象的值在同一个数组位置上,就会出现hash冲突,此时会使用链表来解决冲突。 2. JDK 1.8为什么引入了红黑树? JDK 1.8引入红黑树是为了解决链表过长导致的性能问题。当链表长度超过一定阈值(默认为8),链表会转换为红黑树来提高查找效率。 3. HashMap的内部类Node<K,V>是什么作用? HashMap的内部类Node<K,V>实现了Entry接口,用于存储键值对的数据。每个Node对象表示一个映射关系,包含了键和值。 以上是关于HashMap的一些常见面试题,希望对你有帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [史上最全Hashmap面试总结,51道附带答案,持续更新中...](https://blog.csdn.net/androidstarjack/article/details/124507171)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【硬核】HashMap最全面试题(附答案)](https://blog.csdn.net/weixin_43689480/article/details/118752906)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值