HashMap面试题及答案总结(19道常见必考题)

HashMap面试题及答案总结(19道常见必考题)-mikechen

HashMap是Java面试题的必考题,需要重要来掌握,下面给大家总结了18道HashMap面试题及答案@mikechen

HashMap与HashTable之间的区别

1.HashMap线程不安全、HashTable线程安全,但是使用HashTable在多线程的情况下效率比较偏低,所以在多线程的情况下使用ConcurrentHashMap;

2.多线程的情况下使用HashTable能够保证数据安全性,是采用synchronized锁将整个HashTable中的数组锁住,在多个线程中只允许一个线程访问Put或者Get,效率非常低。

3.多线程的情况下使用HashMap线程不安全,没有上锁,可能会发生一些数据冲突问题,但是效率比较高的。

4.HashMap可以允许存放key值为null,存放在数组第0个位置、HashTable不允许存放的key为null

补充概念“线程安全问题” 多个线程同时访问一个全局共享变量 可能会发生线程安全问题。

为什么重写equals还要重写hashcode方法

1. HashCode 是Object类中的本地方法,底层是基于C语言实现的,方法直接返回对象的内存地址,让后再转换整数。

2. Equals方法

规定:

1. 两个对象的Hashcode值相等,但是两个对象的内容值不一定相等;

2. 两个对象的值Equals比较相等的情况下,则两个对象的Hashcode值一定相等;

== 比较两个对象的内存地址是否相同、Equals默认的情况下比较两个对象的内存地址,

只是我们的String类重写了Equals方法比较两个对象的值是否相同。

HashMap如何避免内存泄漏问题

如果使用自定义对象作为HashMap集合的key,注意需要重写 HashCode 和 Equals 方法、避免内存泄漏问题。

内存泄漏问题:申请堆内存GC一直无法释放引发内存泄漏问题。

HashMap什么HashCode冲突

内容值不相同,但是HashCode值相同,则计算的index值相同(冲突)

HashMap1.7与1.8底层如何实现(Put方法底层实现)(重点)

HashMap面试题及答案总结(19道常见必考题)-mikechen
1.7底层实现基于数组+链表实现(Key和value封装成Entry对象)1.根据key的hash值,计算该key存放在数组的index位置

2.如果发生index冲突,则会使用单向链表存放

同一个链表中存放的都是hashCode值相同,但是内容值却不同

如果index发生冲突,采用链表存放查询的时间复杂度是为O(n),效率非常低,

所以在JDK1.8开始优化改为红黑树

3. 使用头插入法(并发下扩容可能会发生死循环问题)

1.8底层实现

基于数组+链表+红黑树实现(Key和value封装成Entry对象)

1.根据key的hash值,计算该key存放在数组的index位置

2.如果发生index冲突,则会使用单向链表存放

当数组的容量大于=64且链表长度大于8则会将链表转化成红黑树。

红黑树查询的时间复杂度是为O(logN)

当红黑树的节点个数<6则将红黑树转换成链表

3.使用尾插法 解决了HashMap1.7版本并发扩容引发扩容死循环问题。

HashMap1.7与1.8底层如何实现(Get方法底层实现)(重点)

1.HashMap集合1.7版本

会根据我们的index查找到在数组中的链表,会从头到尾部遍历我们链表做比较值是否相等,

如果index没有发生冲突,时间复杂度就是O(1) 只需要查询一次;

如果index发生了冲突,时间复杂度就是为O(N) 会从头查询到尾部 效率非常低。

2. HashMap集合1.8版本

会根据我们的index查找到在数组中的链表或者是红黑树

如果是链表,则时间复杂度为O(N);

如果是红黑树,则时间复杂度为O(logN);

补充概念:O(1) 只需要查询一次 O(N) 从头查询到尾部

HashMapKey为null存放在什么位置

HashMap可以允许存放key值为null,存放在数组第0个位置

为什么HashMap1.8需要引入红黑树(重点)

1.7HashMap集合中,当我们发生了Hash冲突,则会存放在同一个链表中,当链表的查询长度过长,查询效率非常低,因为采用链表存放查询的时间复杂度是为O(n),从头查询到尾部、在JDK1.8开始优化当数组容量>=64且链表长度>8则会将链表转化为改为红黑树,红黑树的时间复杂度为O(logn),性能有所提升。

HashMap1.8链表在什么时候转化成红黑树

当数组的容量大于=64且链表长度大于8则会将链表转化成红黑树。

红黑树查询的时间复杂度是为O(logN)

当红黑树的节点个数<6则将红黑树转换成链表

HashMap1.7扩容死循环的问题有了解过吗

HashMap1.7版本 使用头插入法(并发下扩容可能会发生死循环问题),在HashMap1.8版本采用尾插法解决了该问题。

注意的是:JDK官方不承认该1.7HashMapbug问题,因为在多线程的情况下不推荐使用HashMap而是HashTable或者是ConcurrentHashMap

HashMap底层是有序存放的吗?

HashMap是无序、散列存放 ,遍历的时候从数组0开始遍历每个链表,遍历结果存储顺序不保证一致,如果需要根据存储顺序保存,可以使用LinkedHashMap底层是基于双向链表存放,LinkedHashMap基于双向链表实现HashMap基本单向链表实现

数组:

数组[0]==add(a);

数组[1]==add(b);

数组[2]==add(c);

遍历的结果:abc

HashMap

Key=a index=8;

Key=b index=6;

Key=c index=9

遍历的结果:b,ac

HashMap如何解决Hash冲突问题

JDK1.7版本的HashMap

1.根据key的hash值,计算该key存放在数组的index位置

2.如果发生index冲突,则会使用单向链表存放

同一个链表中存放的都是hashCode值相同,但是内容值却不同

JDK1.8版本的HashMap

1.根据key的hash值,计算该key存放在数组的index位置

2.如果发生index冲突,则会使用单向链表存放,当数组的容量大于=64且链表长度大于8则会将链表转化成红黑树。

根据链表查询key的时间复杂度就是为O(n)—从头查询到尾部

HashMap根据key查询的时间复杂度

key如果没有发生冲突:时间复杂度为O(1)

Key如果发生冲突:链表为O(N),红黑树则是为O(LogN)

HashMap底层如何降低Hash冲突概率

1.高16位做异或运算

Hash值 异或运算:(h = key.hashCode()) ^ (h >>> 16)

2.计算Index值与运算 会实现 i = (n – 1) & hash

最终可以实现减少index冲突的概率。

3.Hashmap用2的n次幂作为容量

HashMap如何存放1万条key效率最高

参考阿里巴巴官方手册:

(需要存储的元素个数 / 负载因子) + 1

10000/0.75+1=13334

正常如果存放1万个key的情况下 大概扩容10次=16384

为什么加载因子是0.75而不是1

产生背景:减少Hash冲突index的概率;

查询效率与空间问题;

简单推断的情况下,提前做扩容:

1. 如果加载因子越大,空间利用率比较高,有可能冲突概率越大;

2. 如果加载因子越小,有可能冲突概率比较小,空间利用率不高;

空间和时间上平衡点:0.75

统计学概率:泊松分布是统计学和概率学常见的离散概率分布

为何Hashmap用2的n次幂作为容量

目的是为了减少hash冲突的概率,实现hashkey 均匀存放。

算法的目的是为了得到小于length的更加均匀的数,如果不均匀容易产生hash碰撞,换句话说只有全是1,进行按位与才是最均匀的,因为1与上任何数都等于任何数本身

为什么是length-1不是length了

16是10000 15是01111。16与任何数只能是0或者16。15与任何数等于小于16的任何数本身。

为什么容量是2的n次方呢

2的n次方一定是最高位1其它低位是0,

这样减1的时候才能得到01111这样都是1的二进制。

如何在高并发的情况下使用HashMap

不建议使用HashTable效率偏低,建议使用ConcurrentHashMap。

ConcurrentHashMap底层实现的原理

1.使用传统HashTable保证线程问题,是采用synchronized锁将整个HashTable中的数组锁住,

在多个线程中只允许一个线程访问Put或者Get,效率非常低,但是能够保证线程安全问题。

2.在多线程的情况下推荐使用ConcurrentHashMap

ConcurrentHashMap1.7实现原理

HashMap面试题及答案总结(19道常见必考题)-mikechen

1. ConcurrentHashMap采用分段锁设计、将一个大的HashMap集合拆分成n多个不同的小的HashTable(Segment),默认的情况下是分成16个不同的Segment,每个Segment中都有自己独立的HashEntry<K,V>[] table;

2. 数组+Segments分段锁+HashEntry链表实现

3. 使用Lock锁+CAS乐观锁+UNSAFE类

4.PUT方法流程

A. 第一次需要计算出:key出存放在那个Segment对象中

B. 还需要计算key存放在Segment对象中具体index位置。

ConcurrentHashMap1.8实现原理

HashMap面试题及答案总结(19道常见必考题)-mikechen

Put原理 锁的粒度非常小,对每个数组index位置上锁 对1.7ConcurrentHashMap实现优化

1. 取消segment分段设计,使用synchronized锁

2. synchronized在JDK1.6开始做了优化 默认实现锁的升级过程

 

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法