【第二章:Java核心技术解析】第6节:Java基础 - 三大集合(上)


大家好,很高兴我们可以继续学习交流Java高频面试题。本小节是Java基础篇章的第四小节,主要介绍Java中的常用集合知识点,涉及到的内容包括Java中的三大集合的引出,以及HashMap,Hashtable和ConcurrentHashMap

本小节内容几乎是Java面试中必考的点,或者说是你必须要熟练掌握的知识点。在实际的开发的工作中,我们经常借助集合完成数据的排序,查找等操作。熟练掌握Java中的常用集合,对于实际开发工作效率的提升也很有帮助。

我们先来介绍下Java中集合知识的整体情况吧。

三大集合接口的引出:

Java中的集合,从上层接口上看分为了两类,Map和Collection。也就是说,我们平时接触到的常用的集合,包括HashMap,ArrayList和HashSet等都直接或者间接的实现了这两个接口之一。而Collection接口的子接口又包括了Set和List接口。这样我们常见的Map,Set和List三大集合接口就出来了。接口类图如下所示:

图片说明

这个时候,比较“机灵”的面试官就会发问了。

面试官:Map,List和Set都是Collection的子接口吗?

答:Map是和Collection并列的集合上层接口,没有继承关系;List和Set是Collection的子接口。

在本小节的附图中,我们给出了本节所涉及到的集合的类图结构,列出来是为了大家学习的时候方便查阅,接下来我们结合面试题来进行各个知识点的解析吧。

(1)说说Java中常见的集合吧。

答:Java中的常见集合可以概括如下。

  • Map接口和Collection接口是所有集合框架的父接口
  • Collection接口的子接口包括:Set接口和List接口
  • Map接口的实现类主要有:HashMap、TreeMap、Hashtable、LinkedHashMap、ConcurrentHashMap以及Properties等
  • Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
  • List接口的实现类主要有:ArrayListLinkedList、Stack以及Vector等

(2)HashMap和Hashtable的区别有哪些?

答: HashMap和Hashtable之间的区别可以总结如下。

  • HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
  • HashMap允许null作为Key;Hashtable不允许null作为Key,Hashtable的value也不可以为null

解析:
这个算是面试官针对HashMap的一个开胃小菜,重点是根据候选人的回答进行下一步的考察。既然候选人说出了线程安全和不安全的区别,面试官会接着考察线程安全的具体含义,如下所示:

HashMap是线程不安全的是吧?你可以举一个例子吗?

答:(注意,以下是候选人常见的错误理解!!!,因为上边的答案是大家背出来的)
有一个快速失败fast-fail机制,当对HashMap遍历的时候,调用了remove方法使其迭代器发生改变的时候会抛出一个异常ConcurrentModificationException。Hashtable因为在方法上做了synchronized处理,所以不会抛出异常。(自信的语气^_^感觉面试官很low)

我们这里先给出正确答案:

  • HashMap线程不安全主要是考虑到了多线程环境下进行扩容可能会出现HashMap死循环
  • Hashtable线程安全是由于其内部实现在put和remove等方法上使用synchronized进行了同步,所以对单个方法的使用是线程安全的。但是对多个方法进行复合操作时,线程安全性无法保证。 比如一个线程在进行get然后put更新的操作,这就是两个复合操作,在两个操作之间,可能别的线程已经对这个key做了改动,所以,你接下来的put操作可能会不符合预期。

既然说到了这里,那么我们来看看大家一直想说的Java集合快速失败(fast-fail)机制是怎么回事儿吧~

Java集合中的快速失败(fast-fail)机制:

答:快速失败是Java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast。

例如:

假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就可能会抛出 ConcurrentModificationException异常,从而产生fast-fail快速失败。

那么快速失败机制底层是怎么实现的呢?

迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedModCount值,是的话就返回遍历;否则抛出异常,终止遍历。JDK源码中的判断大概是这样的:

图片说明

我们再来接着看异常ConcurrentModificationException,JDK中是这么介绍该异常的:

图片说明

图片说明

我来解释下JDK中的英文,大概意思就是说当检测到一个并发的修改,就可能会抛出该异常,一些迭代器的实现会抛出该异常,以便可以快速失败。但是你不可以为了便捷而依赖该异常,而应该仅仅作为一个程序的侦测。

前面常见的错误答案,错误的认为快速机制就是HashMap线程不安全的表现。并且坚定的认为Hashtable和Vector等线程安全的集合不会存在并发修改时候的快速失败,这是大错特错。概念和原理理解的不清晰导致掉入了面试官的陷阱里了,大家可以打开JDK源码,会发现Hashtable也会在迭代的时候抛出该异常,可能发生快速失败。


(3)HashMap底层实现结构有了解吗?

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java开发岗高频面试题全解析 文章被收录于专栏

<p> Java开发岗高频面试题全解析,专刊正文共计31节,已经全部更新完毕。专刊分9个模块来对Java岗位面试中的知识点进行解析,包括通用面试技能,Java基础,Java进阶,网络协议,常见框架以及算法,设计模式等。专刊串点成面的解析每个面试题背后的技术原理,由浅入深,循序渐进,力争让大家掌握面试题目的背后的技术原理,摒弃背题模式的陋习。 专刊详细信息,请查阅专刊大纲和开篇词的介绍。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p> <p> <br /> </p>

全部评论
ConcurentHashMap是重中之重吧 不管是1.7的分段锁还是1.8的CAS 希望能讲一讲
8 回复 分享
发布于 2019-12-02 21:54
HashMap和Hashtable的区别有哪些? 1.是否线程安全 2.是否可以存null键,null值 3.初始容量,以及扩容方式      HashTable的默认初始容量是11, 扩容是2n+1     HashMap的默认初始容量是16,扩容是2*n
3 回复 分享
发布于 2020-06-20 11:49
hashtable复合操作,多线程这块是不是描述的有什么问题呢?获取的不是对象锁么?
2 回复 分享
发布于 2020-03-07 21:24
老师,想问一下HashMap扩容死循环的问题是因为头插法嘛,JDK1.8改用了尾插法就不会出现死循环的问题了,那么HashMap这个线程安全问题应该怎么答呢?还有面试的时候会让我们详细的说出具体的死循环过程嘛?
2 回复 分享
发布于 2020-02-25 11:59
打卡 一刷。  不知道有没有理解错:  HashMap,1.7 不安全,因为并发操作时,会出现并发修改异常;并且因为采用头插法,扩容时会造成死循环问题。 HashMap,1.8 不安全,虽然改用尾插法解决了死循环问题,但是还是存在并发修改异常的的问题。
1 回复 分享
发布于 2020-09-09 14:10
jdk 1.8 使用更加细粒度的,CAS、synchronized
1 回复 分享
发布于 2020-08-18 14:58
刚刚面试遇到了 这道题题目   真是高频  
1 回复 分享
发布于 2020-07-23 20:43
打卡,对于ConcurrentHashMap 1.8的CAS想多了解一点
1 回复 分享
发布于 2020-07-01 23:52
老师,想问一下,HashMap扩容的元素个数是指(包括数组和链表+红黑树中)。还是指链表中的个数(在别的地方看到的)。
1 回复 分享
发布于 2020-02-24 21:07
打卡
1 回复 分享
发布于 2020-02-09 15:17
hashmap应该是在容量小于八的时候使用链表,超过8时会改变结构变成红黑树。可以看一下put的流程图有这个操作
1 回复 分享
发布于 2020-01-11 22:17
打卡o(* ̄▽ ̄*)o
1 回复 分享
发布于 2019-11-25 17:10
老师你好,看了你的面试题解析获益匪浅,这里有个问题哈,内存分析图中图(4),strBuf和sb2指向的应该是Hi,I am World!,您这边应该是漏打了World了,嘻嘻,希望秋招末尾能上岸。
1 回复 分享
发布于 2022-10-13 23:25 广东
今天面试问了list map set 的区别,我忘了,忘了啊😭
点赞 回复 分享
发布于 2023-07-14 15:36 江西
请问一下,ConcurrentHashMap类中包含两个静态内部类HashEntry和Segment,还是MapEntry和Segment。
点赞 回复 分享
发布于 2021-04-01 00:38
在与h的二进制与操作效率会非常的快,而且空间不浪费 这个  在于h  的二进制中的  h   什么意思,是不是写错了
点赞 回复 分享
发布于 2021-03-07 17:19
老师你是不是自己都没搞明白JDK1.8的concurrenthashmap原理?这个太难了
点赞 回复 分享
发布于 2021-03-05 12:31
打卡
点赞 回复 分享
发布于 2021-01-18 01:32
ConcurrentHashMap你说的是1.7版本的吧 ,数组+Segment+分段锁; 1.8 已经采用 CAS+synchronized+HashEntry+红黑树了
点赞 回复 分享
发布于 2021-01-16 17:10
集合
点赞 回复 分享
发布于 2020-12-21 10:49

相关推荐

点赞 评论 收藏
分享
码农索隆:这种hr,建议全中国推广
点赞 评论 收藏
分享
评论
12
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务