Java 集合框架面试复习题
2025/12/19大约 6 分钟约 1910 字
Java 集合框架面试复习题
闲时回忆题目列表
一、集合框架整体结构
1.1 基础概念
- Q1: 请描述 Java 集合框架的整体架构,主要有哪些顶层接口?
- Q2: Collection 和 Map 有什么区别?为什么 Map 不继承 Collection?
- Q3: Iterable、Iterator、ListIterator 三者有什么区别?
- Q4: RandomAccess 接口有什么作用?哪些集合实现了它?
- Q5: 什么是 Fail-Fast 机制?modCount 的作用是什么?
- Q6: Fail-Fast 和 Fail-Safe 有什么区别?各有哪些代表集合?
二、List 接口
2.1 ArrayList
- Q1: ArrayList 的底层数据结构是什么?
- Q2: ArrayList 的默认初始容量是多少?什么时候分配?
- Q3: ArrayList 的扩容机制是怎样的?扩容倍数是多少?
- Q4: JDK 1.7 和 1.8 在 ArrayList 初始化上有什么区别?(懒加载)
- Q5: ArrayList 的
add()、get()、remove()的时间复杂度分别是多少? - Q6: 为什么 ArrayList 的
elementData用transient修饰? - Q7:
Arrays.asList()返回的 List 能否增删元素?为什么? - Q8: 如何在遍历 ArrayList 时安全地删除元素?
2.2 LinkedList
- Q1: LinkedList 的底层数据结构是什么?
- Q2: LinkedList 为什么同时实现了 List 和 Deque 接口?
- Q3: LinkedList 的
get(int index)时间复杂度是多少?它做了什么优化? - Q4: LinkedList 和 ArrayList 如何选择?各自适用什么场景?
2.3 ArrayList vs LinkedList
- Q1: 从数据结构、随机访问、内存占用、CPU 缓存、增删效率等方面对比两者。
- Q2: LinkedList 的头部插入真的比 ArrayList 快吗?为什么?
- Q3: 为什么大多数情况推荐使用 ArrayList?
2.4 Vector 与 Stack
- Q1: Vector 和 ArrayList 有什么区别?为什么 Vector 现在不推荐使用?
- Q2: Stack 继承自哪个类?为什么现在推荐用 Deque 替代 Stack?
三、Set 接口
3.1 HashSet
- Q1: HashSet 的底层实现是什么?元素存储在哪里?value 是什么?
- Q2: HashSet 如何判断元素是否重复?
- Q3: 为什么重写
equals()必须同时重写hashCode()? - Q4: HashSet 能存储 null 吗?能存几个?
3.2 LinkedHashSet
- Q1: LinkedHashSet 和 HashSet 有什么区别?
- Q2: LinkedHashSet 如何保持插入顺序?底层结构是什么?
3.3 TreeSet
- Q1: TreeSet 的底层数据结构是什么?
- Q2: TreeSet 如何保证元素有序?有哪两种排序方式?
- Q3: TreeSet 能存储 null 吗?为什么?
- Q4: TreeSet 的增删查时间复杂度是多少?
四、Queue 接口
4.1 Queue 基础
- Q1:
add()和offer()有什么区别? - Q2:
remove()和poll()有什么区别? - Q3:
element()和peek()有什么区别?
4.2 PriorityQueue
- Q1: PriorityQueue 的底层数据结构是什么?
- Q2: PriorityQueue 默认是大顶堆还是小顶堆?如何变成大顶堆?
- Q3: PriorityQueue 的插入、删除、获取堆顶的时间复杂度分别是多少?
- Q4: 遍历 PriorityQueue 能得到有序的结果吗?为什么?
4.3 Deque(双端队列)
- Q1: Deque 接口定义了哪些操作?它可以用作哪些数据结构?
- Q2: ArrayDeque 和 LinkedList 作为 Deque 使用时有什么区别?哪个更推荐?
- Q3: ArrayDeque 的底层数据结构是什么?它如何实现循环数组?
- Q4: 为什么推荐用 ArrayDeque 替代 Stack?
五、Map 接口
5.1 HashMap(重点!)
基础原理
- Q1: HashMap 在 JDK 1.7 和 1.8 中的数据结构有什么区别?
- Q2: HashMap 的默认初始容量和负载因子分别是多少?
- Q3: 为什么 HashMap 的容量必须是 2 的幂次方?
- Q4: HashMap 的 hash 扰动函数是什么?为什么需要扰动?
链表与红黑树
- Q5: 链表转红黑树的条件是什么?为什么要双重判断?
- Q6: 为什么链表阈值是 8?(泊松分布)
- Q7: 红黑树退化为链表的条件是什么?
put 与 get 流程
- Q8: 详细描述 HashMap 的
put()方法执行流程。 - Q9: HashMap 如何计算 key 的存储位置?
- Q10: 1.7 和 1.8 中 put 元素到链表使用的是头插还是尾插?为什么 1.8 改了?
扩容机制
- Q11: HashMap 在什么条件下会触发扩容?
- Q12: HashMap 扩容时,元素如何重新分配位置?(1.8 高低位链表优化)
- Q13: 为什么 1.7 的 HashMap 多线程扩容可能导致死循环?
其他
- Q14: HashMap 允许 key 或 value 为 null 吗?
- Q15: HashMap 是线程安全的吗?如何让它线程安全?
5.2 LinkedHashMap
- Q1: LinkedHashMap 和 HashMap 有什么区别?
- Q2: LinkedHashMap 如何保持顺序?有哪两种顺序模式?
- Q3: 如何用 LinkedHashMap 实现 LRU 缓存?
5.3 TreeMap
- Q1: TreeMap 的底层数据结构是什么?
- Q2: TreeMap 如何保证 key 有序?
- Q3: TreeMap 的增删查时间复杂度是多少?
5.4 Hashtable
- Q1: HashMap 和 Hashtable 有什么区别?
- Q2: 为什么现在不推荐使用 Hashtable?
5.5 ConcurrentHashMap(重点!)
JDK 1.7 版本
- Q1: JDK 1.7 的 ConcurrentHashMap 采用什么数据结构?
- Q2: 什么是分段锁(Segment)?它继承自什么类?
- Q3: JDK 1.7 的并发度是多少?可以调整吗?
JDK 1.8 版本
- Q4: JDK 1.8 的 ConcurrentHashMap 采用什么数据结构?
- Q5: JDK 1.8 用什么机制保证线程安全?(CAS + synchronized)
- Q6: 为什么 JDK 1.8 要用 synchronized 而不是 ReentrantLock?
- Q7: ConcurrentHashMap 的
get()方法需要加锁吗?为什么? - Q8: ConcurrentHashMap 的
put()流程是怎样的?
对比
- Q9: JDK 1.7 和 1.8 的 ConcurrentHashMap 有什么主要区别?
- Q10: ConcurrentHashMap 能存储 null 的 key 或 value 吗?为什么?
六、并发集合
6.1 CopyOnWriteArrayList
- Q1: CopyOnWriteArrayList 的实现原理是什么?
- Q2: CopyOnWriteArrayList 适用于什么场景?有什么缺点?
- Q3: CopyOnWriteArrayList 和 Collections.synchronizedList 有什么区别?
6.2 CopyOnWriteArraySet
- Q1: CopyOnWriteArraySet 的底层实现是什么?
6.3 BlockingQueue
- Q1: BlockingQueue 有哪些常见的实现类?各有什么特点?
- Q2: 阻塞队列的四种操作方式(抛异常、返回值、阻塞、超时)分别对应哪些方法?
- Q3: ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别?
- Q4: 如何用 BlockingQueue 实现生产者-消费者模式?
6.4 ConcurrentLinkedQueue
- Q1: ConcurrentLinkedQueue 是阻塞队列吗?它用什么机制保证线程安全?
七、工具类
7.1 Collections
- Q1:
Collections.sort()使用的是什么排序算法? - Q2:
Collections.synchronizedXxx()系列方法的原理是什么? - Q3:
Collections.emptyList()和new ArrayList()有什么区别? - Q4:
Collections.unmodifiableList()返回的 List 有什么特点?
7.2 Arrays
- Q1:
Arrays.asList()返回的 List 是什么类型?能进行增删操作吗? - Q2:
Arrays.sort()对基本类型和对象类型分别使用什么排序算法? - Q3:
Arrays.copyOf()和System.arraycopy()有什么区别?
八、综合对比题
8.1 选择题类型
- Q1: ArrayList、LinkedList、Vector 如何选择?
- Q2: HashSet、LinkedHashSet、TreeSet 如何选择?
- Q3: HashMap、LinkedHashMap、TreeMap、Hashtable 如何选择?
- Q4: ArrayDeque 和 LinkedList 作为队列时如何选择?
- Q5: HashMap 和 ConcurrentHashMap 如何选择?
8.2 原理对比
- Q1: hashCode() 和 equals() 的关系是什么?为什么要一起重写?
- Q2: Comparable 和 Comparator 的区别是什么?
- Q3: Iterator 和 ListIterator 的区别是什么?
九、场景设计题
- Q1: 如何设计一个线程安全的 LRU 缓存?
- Q2: 如何实现一个固定大小的阻塞线程安全队列?
- Q3: 如何统计一篇文章中每个单词的出现次数?用什么集合?
- Q4: 如何实现一个能快速查找、有序输出的电话簿?
- Q5: 如何合并两个有序数组?
附录:常见时间复杂度速查表
| 集合 | get | add/put | remove | contains | 有序 |
|---|---|---|---|---|---|
| ArrayList | O(1) | O(1)* | O(n) | O(n) | 插入序 |
| LinkedList | O(n) | O(1)** | O(1)** | O(n) | 插入序 |
| HashSet | - | O(1) | O(1) | O(1) | 无 |
| LinkedHashSet | - | O(1) | O(1) | O(1) | 插入序 |
| TreeSet | - | O(log n) | O(log n) | O(log n) | 排序 |
| HashMap | O(1) | O(1) | O(1) | O(1) | 无 |
| LinkedHashMap | O(1) | O(1) | O(1) | O(1) | 插入/访问序 |
| TreeMap | O(log n) | O(log n) | O(log n) | O(log n) | Key 排序 |
| PriorityQueue | O(1)*** | O(log n) | O(log n) | O(n) | 堆序 |
*: 均摊 O(1),扩容时 O(n)
**: 已知位置时 O(1),需查找时 O(n)
***: peek() 是 O(1)
