面试连环炮:从HashSet开始,一路怼到CPU

504 篇文章 3 订阅
503 篇文章 11 订阅

之前在金三银四,我面试了20家互联网公司这篇文章中提到我在今年四月份面试了大大小小近20家互联网公司,然后总结出来一套由浅入深的Java基础面试题连环炮。本文主要给出这些面试题的答案。

由浅入深了解这些面试题背后的知识基本能hold住80%的Java面试题,在此基础上再去学习拓展其他Java相关知识点,核心是把自己所掌握的零零散散的知识点串联起来,形成自己的知识体系。

系列面试题如下:

 

接下来我们就给出每一题的答案,希望大家在这个过程中能够有所贯通。

本文所有内容都是基于Jdk1.8版本。建议大家收藏后持续阅读!关注“java架构设计”阅读更多技术文章~

HashSet的底层是用什么来实现的?

HashSet一定是大家常用的一种数据结构,但是你在天天用的时候,有没有想过它的底层到底是怎么实现的呢?有没有点击进去看看JDK的源码呢?

如果大家英文阅读能力ok,看源码的时候一定要看看类注释(英文不好的可以借助翻译工具)!比如打开java.util.HashSet类源码,类注释第一段是这样的:

This class implements the <tt>Set</tt> interface, 
backed by a hash table (actually a <tt>HashMap</tt> instance).  
It makes no guarantees as to the iteration order of the set; 
in particular, it does not guarantee that the order will remain constant over time.  
This class permits the <tt>null</tt> element.

翻译过来就是:

这个类实现了由哈希表支持的Set接口

(实际上是一个HashMap实例)。它不能保证

集合的迭代顺序;特别是,它不能保证

随着时间的推移,秩序将保持不变。这个类允许null元素。

再看它的各种构造方法:

 

就能知道它的底层实现就是一个HashMap!

那么HashSet的底层是用什么来实现的是不是就很清晰了,问题到此结束,不再拓展,感兴趣的同学可以继续看源码。

对HashSet集合装入的对象有什么要求?

这道题很简单,但是也很绕。尤其是我们在面试的时候,紧张的状态下很多时候头脑是不够清晰的,那么就容易get不到这个点。

我们经常会用一个HashSet来装入一组数据,比如这样:

Set<User> users = new HashSet<User>(10);
User user = new User();
users.add(user);

问的就是这个User对象有什么要求?首先我们要了解HashSet集合的特性:

  1. 无序的
  2. 不可重复的

为什么是无序的?第一题已经说到了,他用的是HashMap,其实就是HashMap里面的key,那自然就是无序的了。

如何保证不可重复的?换句话说,我怎么判定一个对象在集合里面是否已经存在了?

相信大家会立马就知道一个对象是否相等需要根据hashCode()和equals()方法来判断,这也就是为什么说一个对象要重写它的hashCode()方法和equals()方法了。

比如java.lang.String类一定有它自己的hashCode()和equals()方法。

那么这题的答案就是:一定要重写它的hashCode()和equals()方法!

get到这个点,相信大家对hashCode()和equals()方法的相关问题一定是手到擒来,了如指掌了吧?

HashMap的实现原理?

大家有没有发现,上面说来说去说的其实还是HashMap,这也就是说为什么是面试连环炮,你能聊下去,自然就聊到HashMap了。

有的同学说我就不按照你的套路来,就聊不下去?那你觉得这次面试还有戏吗?

所以这个套路是跑不掉的,不聊HashMap我怎么和你聊多线程?怎么和你聊安全的Map?

所以,不管怎么学,大家一定要把HashMap的底层原理、jdk源码烂熟于心,把底子打好!这样才有资格去继续学习后面的ConcurrentHashMap、synchronized、CAS、Lock、ThreadLocal等等。

关于HashMap的底层实现原理,这里不赘述,大家可以直接看我的另外一篇文章:

如果你这么去理解HashMap就会发现它真的很简单

了解HashMap原理后再回头看这些问题:

HashMap中计算元素的下标公式为什么是hash(key) & (size - 1)?

这里面有几个知识点:

  1. hash(Object key)方法的实现
  2. 和hashMap的size-1做与运算
  3. 为什么是size-1?
  4. HashMap的容量为什么都是2的n次方?

 

 

简单来说第一个知识点就是为了得到的hashCode更加的散列:key的hashCode值和高16位做异或运算,来使得低位的值更加的散列。

更加散列的目的就是为了和size-1做与运算的时候分布更均匀!

为什么是size-1?为什么容量是2的n次方?

也就是HashMap的容量只会是4、8、16、32这样2倍扩容,那么size-1就是对应的3、7、15、31,换算成二进制便是:11、111、1111、11111,所以在做与运算的时候,一定就是取hash(Object key)的低几位!

HashMap是线程安全的吗?

当然不是!是的话还有后面的HashTable、ConcurrentHashMap什么事情?

HashMap不是线程安全的,他的get、put操作都没有加锁,意味着多线程环境下,无法保证put进去的值,get之后还是这个值。

那你知道哪些线程安全的Map吗?

  1. Collections.synchronizedMap(Map<k, v> map)
  2. HashTable
  3. ConcurrentHashMap

它们都是怎么来保证线程安全的?

  1. Collections有一个内部静态类SynchronizedMap,它里面有一个Map和mutex排斥锁。这个时候再去操作map的时候,相关方法全部加上了synchronize(mutex)。
  2. HashTable是线程安全的,他对数据操作的时候也就是put时候都会上锁。
  3. 在1.7中,ConcurrentHashMap有一个内部类Segment,他继承了ReentranLock可重入锁,Segment有点类似于HashMap,它里面有一个HashEntry数组,整体结构还是数组+链表,但是这个数组是用volatile实现的。在1.8之后就不是了,而是采用CAS+Synchronized来保证并发安全性。

 

synchronized、锁升级、CAS、Lock锁

通过上面的HashMap、ConcurrentHashMap自然就引入到synchronized关键字了。

以前都说它慢,是个重量级锁,需要向操作系统申请mutex锁资源,为什么现在一直在用呢?

这个时候就得去了解锁升级了,jdk1.6版本以后对sychronized的优化就是锁升级的过程,大多数情况下,无锁即可满足我们的业务场景。只有在极其苛刻的条件下才会升级为重量级锁。

那为什么重量级锁会慢呢?这就怼到CPU层了,应用程序会向操作系统排队申请Mutex锁资源。

这又涉及到用户态和内核态的切换了。。。

锁升级的过程大家可以专门找一篇文章去详细了解,也是大厂面试装B必问,这里不赘述。

同理锁升级的过程中就是CAS自旋锁的步骤,所以CAS也得必知必会呀。。。

还有上面提到的1.7版本中ConcurrentHashMap里面使用到的ReentrantLock可重入锁,每一块进入都是一个全新的世界!

而需要我们做的就是从线头开始一点一点的梳理、挖掘出这些知识点,形成我们自己所独有的技术线。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值