集合框架面试题
目录概述
集合框架是 Java 最常用的 API,也是面试官探测候选人源码阅读能力的最佳切入点。HashMap、ConcurrentHashMap、ArrayList,每一个都能追问到 JDK 源码层面。
这个目录不是给你背答案的地方,而是让你站在面试官的角度,理解追问链的逻辑和判分标准。
【面试官心理】 我出集合题,不是在考你背源码。我是想知道你有没有亲手看过代码,能不能把原理和实战串联起来。能答出 put 流程的人很多,能说清楚 JDK 8 为什么引入红黑树的才是真正理解的。
内容范围
核心主题
Map 接口家族:
- HashMap 数据结构:数组 + 链表 + 红黑树
- HashMap put 流程:hash 计算、桶定位、链表/红黑树插入、扩容判断
- HashMap hash 算法:
hash & (length - 1)替代取模的原理 - HashMap 扩容机制:JDK 7 头插法死循环问题、JDK 8 尾插法优化
- HashMap 树化阈值:为什么是 8?红黑树退链表条件
- HashMap 线程安全:JDK 7 并发问题、JDK 8 的修复
- Hashtable vs HashMap:synchronized vs 无锁、效率差异
- ConcurrentHashMap JDK 8 原理:CAS + synchronized、分段锁 vs 无锁
List 接口家族:
- ArrayList 扩容机制:
oldCapacity + (oldCapacity >> 1),1.5 倍扩容 - ArrayList vs LinkedList:随机访问 vs 插入删除、空间局部性原理
- CopyOnWriteArrayList:读写分离思想、适用场景
- Fail-Fast 机制:modCount 计数器、并发修改检测
Queue 接口家族:
- PriorityQueue:堆结构、二叉堆实现
- DelayQueue:延迟队列实现、定时任务应用
- BlockingQueue:生产者-消费者模式
面试题分级
高频必考题 🔴
HashMap put 流程
面试官问:"HashMap 的 put 流程说一下。"
这道题太基础了,几乎每个候选人都能背出来。但我追问的不是流程本身,而是"JDK 8 为什么要引入红黑树"。很多人会说"因为链表太长影响性能"。我追问:"多长算长?为什么是 8 不是 16?" 能答到这层的候选人不到 30%。
【面试官心理】 我问他 put 流程,其实不是想听背书。我想知道的是:他有没有亲手看过源码,能不能理解 JDK 8 为什么要引入红黑树。链表阈值 8、树化条件、链表退化的平衡——这才是 P6 和 P5 的差距所在。
HashMap 容量为什么是 2 的幂次
面试官问:"HashMap 的容量为什么是 2 的幂次?"
大多数人能说出"为了 hash 散列均匀"。我追问:"怎么散列?hashCode 返回的是 int,为什么还要再计算一次?" 能说出 hash & (length - 1) 能代替取模、以及"2 的幂次 - 1 的二进制全是 1"这个根本原因的候选人不到 10%。
ConcurrentHashMap 线程安全
面试官问:"ConcurrentHashMap 是怎么保证线程安全的?JDK 7 和 JDK 8 有什么区别?"
这道题能答出分段锁的占 60%,能说清楚 JDK 8 用 CAS + synchronized 替代分段锁的占 30%,能解释为什么这么改的只有 10%。
ArrayList 扩容机制
面试官问:"ArrayList 的默认容量是多少?扩容倍数是多少?"
很多人脱口而出"默认容量 16",错。ArrayList 默认容量是 0,第一次 add 时才扩容到 10。我追问:"为什么 JDK 选择 10 而不是 16?grow() 方法里的 oldCapacity + (oldCapacity >> 1),1.5 倍扩容,为什么不用 2 倍?" 能答到这层的候选人凤毛麟角。
中频常考题 🟡
- HashMap vs Hashtable:synchronized 性能问题
- CopyOnWriteArrayList:读写分离的代价
- ArrayList vs LinkedList:空间局部性原理
- Fail-Fast 机制与并发修改
低频了解题 🟢
- Queue/Deque 接口分类
- PriorityQueue 堆结构
- LinkedHashMap 缓存淘汰
学习路径指引
P5 候选人(校招/初级社招)
先搞定 HashMap 和 ArrayList 的基本原理。能背出 put/get 流程,知道扩容机制和线程安全问题。这个阶段不需要死磕源码,但要把核心流程讲清楚。
P6 候选人(中级社招)
必须过一遍 JDK 8 HashMap 源码。重点理解:hash 算法、树化阈值设计、扩容时的 rehash 逻辑、并发问题(resize 死循环)。ConcurrentHashMap 要能讲清楚 JDK 7 分段锁和 JDK 8 CAS + synchronized 的区别。
P7 候选人(高级/架构方向)
除了原理,要有关于选型的实战经验。什么时候用 HashMap、什么时候用 ConcurrentHashMap、什么场景下其他 Map 实现更合适。能讲清楚生产踩坑案例和排查过程。
【面试官心理】 P7 候选人被问到这个部分,我通常会问:"你在项目里用过什么替代方案吗?为什么很多开源框架用的是自己的 HashMap 实现?" 能答到这层的,基本都是有大型项目经验的人。
生产避坑
死循环问题
JDK 7 HashMap 在并发场景下扩容时,采用头插法移动元素,可能导致链表成环,引发死循环。JDK 8 用尾插法解决了这个问题,但不是线程安全的——只是不会死循环了,数据错乱仍然存在。
容量预设
线上事故中,HashMap 扩容导致性能问题的案例最多。40 万数据的 HashMap 可能扩容 18 次,每次都要重新计算所有元素的 hash 位置。正确的做法是根据预估容量预设 initialCapacity。
内存泄漏
HashMap 作为本地缓存时,如果没有正确处理过期 key,可能导致内存持续增长。解决方案:WeakHashMap、 expiration 机制、定期清理。