【面试篇】HashMap常见面试题目

本文探讨了HashMap的原理、1.7与1.8的区别,一致性哈希算法在分布式缓存中的应用,以及Java容器(尤其是ConcurrentHashMap)的线程安全问题、分段锁和优化。重点讲解了哈希冲突的处理、数据结构选择、面试常见题和实战场景。
摘要由CSDN通过智能技术生成

【面试篇】数据结构-哈希表
【面试篇】HashMap常见面试题目
【面试篇】HashMap1.7和HashMap1.8的详细区别对比
【面试篇】ConcurrentHashMap1.8 扩容细节
【面试篇】ConcurrentHashMap1.7和1.8详解对比
什么是一致性哈希算法?如何通俗易懂的了解分布式缓存场景?

1.Java容器有哪些?

Java容器类库定义了两个不同概念的容器:CollectionMap

a.Collection

一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素;Set不能有重复元素;Queue按照排队规则来确定对象产生的顺序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jPVvYiIO-1606646952621)(C:\Users\lcz\Downloads\Collection.png)]

b.Map

一组成对的“键值对”对象,允许使用键来查找值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-na7eskpr-1606646952624)(C:\Users\lcz\Downloads\Map.png)]

2.HashMap的常见题目

HashMap = 数组+链表+红黑树

HashMap是基于Hash算法实现的,通过put(key,value)来存储,get(key)来获取。当传入key时mhashmap会根据key,通过hashCode()计算出hash值,根据hash值将value保存在bucket中。

a.哈希冲突

当2个不同的key,经过哈希函数计算出相同的结果时。

解决措施:

  • 开放定址法:按照一定规则向其它地址探测,直到遇到空桶;
  • 再哈希法:设计多个哈希函数
  • 链地址法:通过链表将其串起来

Java的JDK1.8解决措施:

HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X1bhuOfv-1606646983501)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129163624819.png)]

b.HashMap数据插入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGbKRKev-1606646983501)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129163824042.png)]

  • 判断数组是否为空,为空进行初始化;

  • 不为空,计算 k 的 hash 值,通过(n - 1) & hash计算应当存放在数组中的下标 index;

  • 查看 table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;

  • 存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据(onlyIfAbsent为false);

  • 如果不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;(如果当前节点是树型节点证明当前已经是红黑树了)

  • 如果不是树型节点,创建普通Node加入链表中;判断链表长度是否大于 8并且数组长度大于64, 大于的话链表转换为红黑树;

  • 插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的二倍。

c.HashMap的初始化

如果HashMap()不传值,默认大小是16,负载因子是0.75.

衡量hashMap需要扩容的条件是:hashMap.size >=capacity x loadfactor

d.HashMap的哈希函数怎么设计的?

hash函数需要先拿到key的hashCode,是一个32位的int值,然后让hashCode的高16位和低16位进行异或操作,然后再与(hashMap.length-1)进行与运算,得到bucket值。

扰动计算:设计原因

  • 一个好的哈希函数要尽可能降低hash碰撞,越分散越好
  • 算法要尽可能高效,所以用位运算。

防止不同hashCode的高位不同但低位相同导致的hash冲突。简单点说,就是为了把高位的特征和低位的特征组合起来,降低哈希冲突的概率,也就是说,尽量做到任何一位的变化都能对最终得到的结果产生影响。

e.java 1.8对hashmap的优化
  • 数组+链表修改成了数组+链表或红黑树:防止链表长度过长,将时间复杂度由O(n)降为O(logn);
  • 链表的插入方式从头插法改为尾插法:因为在1.7头插法扩容时,头插法会时链表发生反转,多线程环境下会产生环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nAeviFiU-1606646983502)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129180501496.png)]

  • 在插入时,1.7线判断是否扩容,再插入;在1.8是先进行插入,插入完成再判断是否还需要扩容

  • java1.7扩容的时候需要对原数组中的元素进行重新hash定位在新数组的位置;而java1.8采用更简单的判断逻辑。

    A:扩容后,若hash值新增参与运算的位=0,那么元素在扩容后的位置=原始位置

    B:扩容后,若hash值新增参与运算的位=1,那么元素在扩容后的位置=原始位置+扩容后的旧位置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WBv4M5XX-1606646983503)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129180921165.png)]

f.那hashMap是线程安全的吗?

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

HashMap的线程不安全主要体现在下面两个方面:
1.在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。
2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。

g.如何解决hashMap的线程不安全的问题?
  • 使用hashtable来替代hashmap

    hashtable是直接在数组上架synchronized关键字,锁住了整个数组。例如:当一个线程使用put方法时,另外一个线程不但不可以使用put方法,连get方法都不可以,效率很低,一般不会使用。

  • 使用Collections类的synchronizedMap(Map<K,V> m)方法可以返回一个线程安全的Map

    例如:通过传入Map之后封装出一个SynchronizedMap对象

    Map<String, Integer> crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());

  • 使用ConcurrentHashMap来定义Map

    其使用分段锁,降低了锁粒度,并发度大大提高。例如package java.util.concurrent;

    public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>

    implements ConcurrentMap<K,V>, Serializable {

    }

h.ConcurrentHashMap的分段锁原理?

在ConcurrentHashMap中成员变量使用volatile修饰,免除了指令重排序,同时保证了内存可见性;

使用了CAS操作和synchronized结合实现赋值操作。

多线程操作只会操作当前操作索引的节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sSRBYqFV-1606646983503)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129183456628.png)]

i.hashMap内部节点是无序的,那么有没有有序的Map?

LinkedHashMap和TreeMap。

  • TreeMap:是按照key的自然顺序或者compartor的顺序进行排序,内部是通过红黑树来排序。所以要么key所属的类实现了comparable接口,要么自定义一个实现了comparator接口的比较器,传给treemap用于key的比较。
  • LinkedHashMap:是在hashmap的基础上,在内部维护了一个单链表,有头尾节点before和after来标识前置节点和后置节点,可以用来实现按插入的顺序或访问的顺序排序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TzhaL5Ft-1606646983504)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201129184806727.png)]

j. hashset的实现原理

hashset是基于hashmap实现的,hashset底层使用hashmap来保存所有元素。hashset不允许重复的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mind_programmonkey

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值