集合框架面试题

目录概述

集合框架是 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:生产者-消费者模式

面试题分级

级别考察重点期望回答判分标准
P5基本原理、API 使用、常见区别能背出流程,不怵简单追问表面正确
P6源码阅读、扩容机制、树化阈值能回答追问,不怵连环追问深度过关
P7并发安全、生产问题、方案选型有实战案例,能讲清 trade-off经验闭环

高频必考题 🔴

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 机制、定期清理。

导航指引