Java基础--HashMap面试最详细知识点总结

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 21:03   4522   0

一开始以为java hashmap的面试问的会比较基础,比较简单,但是最近看了敖丙面试的一个视频,别人面试时回答关于hashmap的知识点总结,深感自己不足。而且面试官问的也比较细,有深度。

原帖:HashMap知识点

HashMap在Java后台开发中面试的频率算很高的。但问的有多细还得看面试官了。

还是需要再继续努力。

这篇就针对面试的常见问题来写知识点吧。

Q:你能说一下HashMap的数据结构吗?

A: Hashmap在1.7和1.8之间做了一个比较大的改变。

在JDK1.7之前,HashMap的数据结构是数组+链表。它的数据节点是一个Entry节点。它的一个内部类。数据的插入过程是头插法(先将原位置的数据后移一位,再将数据插入到该位置)

头插法有什么坏处呢?就是可能会形成死循环。当扩容resize的时候,首先会resize,然后调用transfer方法,把里面的一些Entry进行了rehash,在这个过程中,可能会造成一个链表的循环,就可能在下一次Get中出现一个死循环的情况。也有可能没有加锁,所以在多个线程并发的情况下,不能对数据保证安全。put进去的值取出来还是put进去的一个值。

而JDK1.8之后对HashMap进行了一个比较大的变化。变成了一个链表+数组+红黑树的一个结构(当链表悬挂的>8时自动转成红黑树)。把原来的Entry节点也变成了一个Node节点,它的整个Put过程也做了一个优化。

Q: 你说一下它的扩容机制吧?

A: 先说一下capacity,即容量。在初始化HashMap的时候,如果没有设置它的Capacity,它的默认初始化容量是16,负载因子是0.75,当元素数量超过阈值时就会触发扩容,每次扩容是之前容量的2倍。首次put的时候,先会触发resize扩容,(算是初始化)然后存入数据,然后判断是否需要扩容。不是首次put,则不再初始化,直接存入数据,然后判断是否需要扩容。

Q:1.7之前是头插法,1.8之后是尾插法,头插法的时候会有死循环,那是线程不安全的因素之一吗?

A: HashMap始终是线程不安全的,即使是1.8用了尾插法,也没有改变数据插入的顺序,不会出现链表循环的过程。

Q:那你在开发中都是怎么保证线程安全?

A: 一般是使用ConcurrentHashMap集合容器,或者HashTable,或者加上Synchronized关键字,Lock。

ConcurrentHashmap的并发是更高的。HashTable底层是synchronized保证线程安全,也就是所有访问HashTable的线程必须竞争同一把锁,当某一线程访问HashTable的同步方法时,其他线程也访问,会进入阻塞或者轮询状态。而ConcurrentHashMap底层1.7使用Segment数组和多组HashEntry组成。而1.8直接使用Node数组+链表+红黑树实现。并发控制使用Synchronized和CAS来操作,它只会锁住当前获取到的Entry所在节点的一个值。jdk1.6以后对Synchronized进行了一个锁升级优化的过程。所以效率更高,并发度也更高。

Q:简单介绍一下锁升级

A:无锁状态、偏向锁、轻量级锁、重量级锁。最开始时无锁的状态,一上来会先判断一下。偏向锁,是当前获取到锁资源的线程,会优先让他再去获取到这个锁,如果没有获取到这个锁,就升级成一个轻量级的(加锁和解锁都用到了CAS),一个CAS的锁,一个乐观锁。如果锁竞争激烈, 那么会膨胀成重量级锁,轻量级锁失败后,会进行自旋锁,自旋1.6后自适应,自旋的时间不固定了,最后膨胀成重量级锁,依赖对象内部的monitor锁实现。也是互斥锁。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP