Skip to content

面试题蒸馏卡:Java 集合

github-hxq-note/Java/Java util + 语雀-Java开发/JavaSE/集合 9 篇素材提炼的高频面试题。每题按「标准答 → 追问链 → 常见误解」组织。

Q1:HashMap 的底层原理?

标准答:底层是「数组 + 链表 + 红黑树」。哈希函数 (h=hashCode())^(h>>>16) 算 hash,hash&(length-1) 算索引定位到数组桶;冲突用链表,链表长度 ≥8 且数组 ≥64 时转红黑树(查询 O(n)→O(logn))。

追问链

  • "为什么数组长度是 2 的 n 次方?" → length-1 低位全 1,& 运算分布均匀、碰撞小,且位运算快
  • "为什么负载因子 0.75?" → 时间空间折衷:太小频繁扩容、太大冲突多
  • "链表转红黑树条件?" → 两个都要满足:链表 ≥8 且 table ≥64,否则优先扩容

常见误解:❌ 以为链表一超 8 就树化(还得 ≥64);❌ 以为 HashMap 线程安全。

Q2:HashMap 1.7 和 1.8 的区别?

标准答:三处优化——① 数据结构加红黑树;② 插入从头插法改尾插法;③ 扩容不用重新哈希,位置「不变或 +旧容量」。

追问链

  • "1.7 为什么死循环?" → 头插法 transfer 迁移时链表翻转,多线程形成环形链表
  • "1.8 为什么还会数据覆盖?" → 并发 put 同时判断桶为空都插入,或 ++size 竞争
  • "并发场景用什么?" → ConcurrentHashMap

常见误解:❌ 以为 1.8 就线程安全了(只是不再死循环,数据覆盖还在)。

Q3:ConcurrentHashMap 1.7 和 1.8 的区别?

标准答:核心是「锁粒度变细」——1.7 用 Segment 分段锁(默认 16 段,继承 ReentrantLock),1.8 改 CAS + synchronized(锁 Node) + 红黑树 + 多线程扩容。

追问链

  • "1.8 为什么 get 不用加锁?" → Node 数组 volatile + Unsafe 保证可见性
  • "sizeCtl 是什么?" → 控制初始化/扩容(-1 初始化、-n 有 n-1 线程帮扩容、正数阈值)
  • "1.8 怎么统计 size?" → counterCells 分散热点(同 LongAdder),求和
  • "为什么 CHM 不允许 null?" → 并发下无法区分「不存在」和「值为 null」

常见误解:❌ 以为 1.8 还是分段锁;❌ 以为 CHM 读要加锁。

Q4:ArrayList 和 LinkedList 区别?

标准答:ArrayList 是动态数组,随机访问 O(1) 快、中间增删 O(n) 慢;LinkedList 是双向链表,增删 O(1) 快、随机访问 O(n) 慢。ArrayList 实现 RandomAccess,LinkedList 还实现 Deque。

追问链

  • "ArrayList 扩容机制?" → 无参构造首次 add 扩到 10,之后 1.5 倍(oldCapacity + oldCapacity>>1)
  • "fail-fast 是什么?" → modCount 记录结构修改,遍历中改结构抛 ConcurrentModificationException
  • "什么时候用 LinkedList?" → 频繁头尾增删/当队列栈用;否则 ArrayList 更优(内存局部性)

常见误解:❌ 以为 LinkedList 增删一定快(中间增删要先遍历定位);❌ 以为 ArrayList 初始 10 容量(是懒初始化)。

Q5:HashMap、HashTable、ConcurrentHashMap 区别?

标准答

HashMapHashTableConcurrentHashMap
线程安全是(全表 synchronized)是(分段锁/CAS+synchronized)
null 键值允许不允许不允许
性能低(全表锁)高(细粒度锁)

追问链

  • "为什么不用 HashTable?" → 全表锁并发性能极差,CHM 锁粒度细得多
  • "Collections.synchronizedMap 呢?" → 也是全表锁,只是包装了一层

常见误解:❌ 以为 HashTable 和 CHM 性能差不多(CHM 并发度远高)。

关联

最近更新