集合框架总览#
一句话答案#
集合分
Collection(List/Set/Queue)和Map两大体系;List有序可重复,Set不重复,Map键值对。fail-fast迭代时检测modCount抛ConcurrentModificationException,fail-safe(CopyOnWrite/ConcurrentHashMap)基于副本或弱一致不抛异常。
核心要点
一、两大顶层接口
Iterable
└── Collection
├── List ── ArrayList / LinkedList / Vector
├── Set ── HashSet / LinkedHashSet / TreeSet
└── Queue ── LinkedList / ArrayDeque / PriorityQueue
Map(独立体系,不属于 Collection)
── HashMap / LinkedHashMap / TreeMap / Hashtable / ConcurrentHashMapplaintext二、List / Set / Queue 特性
| 接口 | 有序 | 可重复 | 典型实现 |
|---|---|---|---|
| List | 插入有序、有索引 | ✅ | ArrayList(查改)、LinkedList(增删) |
| Set | 看实现 | ❌ | HashSet(无序)、LinkedHashSet(插入序)、TreeSet(排序) |
| Queue | FIFO/优先级 | ✅ | ArrayDeque、PriorityQueue |
三、fail-fast 机制
- 集合内部维护
modCount(修改计数),迭代器创建时记录expectedModCount。 - 每次
next()校验两者是否相等,不等则抛ConcurrentModificationException。 - 触发场景:遍历时直接调用集合的 add/remove(单线程也会触发)、或多线程并发修改。
for (String s : list) {
if (cond) list.remove(s); // ❌ 抛 ConcurrentModificationException
}
// ✅ 正确:用迭代器自身的 remove
Iterator<String> it = list.iterator();
while (it.hasNext()) { if (cond) it.remove(); }java四、fail-safe 机制
CopyOnWriteArrayList:写时复制,迭代基于旧数组快照,不抛异常但读不到最新写入。ConcurrentHashMap:弱一致迭代器,遍历期间的修改可能可见可能不可见,不抛异常。- 代价:内存开销(副本)或数据弱一致。
五、HashMap vs Hashtable vs ConcurrentHashMap
| 维度 | HashMap | Hashtable | ConcurrentHashMap |
|---|---|---|---|
| 线程安全 | ❌ | ✅(全表 synchronized) | ✅(CAS+桶锁) |
| null 键值 | 允许各一个 null | 都不允许 | 都不允许 |
| 性能 | 高 | 低(整表锁) | 高(分段/桶级并发) |
| 迭代器 | fail-fast | fail-fast | fail-safe(弱一致) |
| 现状 | 主流 | 已淘汰(遗留类) | 并发场景首选 |
面试回答(2分钟版)
Java 集合框架有两大顶层接口,一个是 Collection,一个是 Map,Map 不属于 Collection。Collection 下面分三类:List 是有序可重复、有索引的,常用 ArrayList 和 LinkedList;Set 是不允许重复的,HashSet 无序、LinkedHashSet 保持插入顺序、TreeSet 排序;Queue 是队列,比如 ArrayDeque 和优先级队列 PriorityQueue。Map 是键值对结构,常用 HashMap、保持顺序的 LinkedHashMap、排序的 TreeMap,还有线程安全的 ConcurrentHashMap。面试高频的是 fail-fast 机制:集合内部有个 modCount 修改计数器,迭代器创建时记下期望值,每次 next 都校验,如果遍历过程中直接调用集合的 add 或 remove 改了结构,modCount 对不上就抛 ConcurrentModificationException,注意单线程下遍历时删元素也会触发,正确做法是用迭代器自己的 remove。与之相对的是 fail-safe,像 CopyOnWriteArrayList 写时复制、ConcurrentHashMap 弱一致迭代器,它们遍历的是快照或弱一致视图,不会抛异常但可能读不到最新数据。HashMap、Hashtable、ConcurrentHashMap 的对比也常考:HashMap 线程不安全、允许 null 键值;Hashtable 用整表 synchronized 性能差、已经被淘汰;ConcurrentHashMap 用 CAS 加桶级锁,是并发场景的首选。
追问与易错
追问方向:
- “遍历集合时删除元素为什么报错?”→ 触发 fail-fast,modCount 与迭代器 expectedModCount 不一致;应使用迭代器的 remove 或 removeIf
- “fail-fast 一定能检测到并发修改吗?”→ 不保证。modCount 非 volatile,只是尽力而为的检测机制,不能用作并发正确性保障
- “ArrayList 遍历删除,倒序 for 循环可以吗?”→ 可以。用索引倒序删不依赖迭代器,不会触发 fail-fast
- “Hashtable 为什么被淘汰?”→ 整张表加 synchronized 锁粒度太粗、并发性能差,并发场景应用 ConcurrentHashMap
- “Iterator 和 ListIterator 区别?”→ ListIterator 仅用于 List,支持双向遍历、add、set,并能拿到索引
易错点:
- ❌ “fail-fast 是为了保证线程安全”——它只是检测机制,不保证安全
- ❌ “Set 都是无序的”——LinkedHashSet 保插入序、TreeSet 有序
- ❌ “Map 属于 Collection”——Map 是独立体系
- ✅ 遍历删除统一用
iterator.remove()或removeIf()