HashMap面试题

1.HashMap的底层数据结构?

HashMap底层实现数据结构为数组+链表的形式
 JDK8及其以后的版本中使用了数组+链表+红黑树实现,解决了链表太长导致的查询速度变慢的问题。

简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
   HashMap通过key的HashCode经过扰动函数处理过后得到Hash值,然后通过位运算判断当前元素存放的位置,如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。当Map中的元素总数超过Entry数组的0.75时,触发扩容操作,为了减少链表长度,元素分配更均匀。

2.HashMap是线程安全的吗?

HashMap 是线程安全的.
快速失败 (fast-fail)
“ 快速失败 ” 也就是 fail-fast ,它是 Java 集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。
记住是有可能,而不是一定。
例如:假设存在两个线程(线程 1 、线程 2 ),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生 fail-fast 机制。
在 HashMap 的 forEach 方法中有以下代码:

public void forEach ( BiConsumer <? super K , ? super V > action ) {
	Node < K , V > [] tab ;
	if ( action == null )
		throw new NullPointerException ();
	if ( size > 0 && ( tab = table ) != null ) {
		int mc = modCount ;
	for ( int i = 0 ; i < tab . length ; ++ i ) {
		for ( Node < K , V > e = tab [ i ]; e != null ; e = e . next )
		action . accept ( e . key , e . value );
	}
	if ( modCount != mc )
		throw new ConcurrentModificationException ();
	}
}

在上面我们说到, modCount 是记录每次 HashMap 结构修改。 forEach 方法会在在进入 for 循环之前,将 modCount 赋值给 mc ,如果在 for 循环之后, HashMap 的结构变化了,那么导致的结果就是 modCount != mc ,则抛出 ConcurrentModificationException() 异常。

3.多线程可以用哪种Map

3.1 HashTable

HashTable 中所有 CRUD 操作都是线程同步的。
同样的,线程同步的代价就是效率变低了。

public synchronized V get(Object key) {
       ……
}

public synchronized V put(K key, V value) {
       ……
}

public synchronized V remove(Object key) {
     ……
}
 .....

3.2 ConcurrentHashMap

ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。

底层数据结构
JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。
Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;

实现线程安全的方式(重要):
① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。)
到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;
② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值