集合面试题
1. HashMap 的底层原理?
JDK 1.8 中 HashMap 采用 数组 + 链表 + 红黑树 的数据结构。
- 通过 key 的 hashCode 计算数组下标
- 哈希冲突时,链表长度 < 8 用链表存储,>= 8 且数组长度 >= 64 时转为红黑树
- 默认负载因子 0.75,容量达到 75% 时扩容为原来的 2 倍
- 扩容时重新计算每个元素的位置
2. HashMap 为什么线程不安全?
- JDK 1.7 中,扩容时头插法可能导致环形链表,造成死循环
- JDK 1.8 中,多线程 put 可能导致数据覆盖,造成数据丢失
- 并发环境下建议使用 ConcurrentHashMap
3. ConcurrentHashMap 如何保证线程安全?
JDK 1.7:采用分段锁(Segment),默认 16 个 Segment,每个 Segment 独立加锁,最大支持 16 个线程并发写入。
JDK 1.8:采用 CAS + synchronized 实现,锁粒度更细,只锁住链表或红黑树的首节点。put 时:
- 计算数组下标,若该位置为 null,使用 CAS 插入
- 若不为 null,使用 synchronized 锁住首节点,进行插入操作
4. ArrayList 和 LinkedList 的区别?
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层实现 | 动态数组 | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 增删(头尾) | O(n) | O(1) |
| 内存占用 | 连续内存 | 额外存储前后指针 |
| 适用场景 | 查询多 | 增删多 |
5. HashSet 如何保证元素不重复?
HashSet 底层基于 HashMap 实现,元素作为 HashMap 的 key 存储,value 是一个固定的 Object 对象。利用 HashMap 的 key 唯一性保证元素不重复。