2026 Java实习面试 · 覆盖90%集合考点 · 含源码级答案
标准答案:数组+链表+红黑树。1.8核心优化——链表长度≥8且数组长度≥64时转红黑树,查询从O(n)→O(logn)。退化条件是节点数≤6时转回链表。
// put流程简析 1. 计算hash: (key==null)?0 : (h=key.hashCode())^(h>>>16) 2. 数组为空则resize()初始化 3. (n-1)&hash 定位桶位置 4. 桶为空→直接放;桶有值→判断key相等覆盖/否则链表尾插/红黑树插入 5. size超过threshold则resize()扩容
面试加分:"扰动函数——高16位异或低16位,让hash分布更均匀,减少碰撞。"
标准答案:容量翻倍(oldCap×2),阈值也翻倍。1.8扩容优化——不需要重新hash,原位置要么保持不变,要么移动到原位置+oldCap。resize()通过高位bit判断:新增bit=0则位置不变,=1则位置+oldCap。
// 扩容核心逻辑(简化) for (Nodee : oldTab) { if (e.next == null) newTab[e.hash & (newCap-1)] = e; // 单节点直接重定位 // 链表/红黑树:根据 hash & oldCap 是否为0分流 // =0 → 留在原位 =1 → 移到 j + oldCap }
标准答案:
• JDK 1.7:分段锁Segment(可配16段),继承ReentrantLock,每段独立加锁,并发度=段数
• JDK 1.8:CAS+synchronized,锁粒度为桶的头节点。put时:桶为空→CAS插入;桶有值→synchronized锁头节点后插入。扩容支持多线程并发迁移,效率大幅提升
面试必杀:"1.8去掉了分段锁,采用了更细粒度的桶级别锁+CAS,并发度更高。扩容时多个线程可以同时帮忙迁移数据,这是1.8最大的性能突破。"
标准答案:
• ArrayList:基于动态数组,随机访问O(1),尾部插入O(1),中间插入O(n)。适合频繁查询
• LinkedList:基于双向链表,随机访问O(n),头尾插入O(1)。适合频繁增删(但实际ArrayList在大部分场景下更快,因为LinkedList需要先遍历定位)
• 扩容:ArrayList默认扩容1.5倍(oldCapacity + oldCapacity>>1),LinkedList不需要扩容
面试陷阱:"都说不适合频繁增删用LinkedList,但LinkedList需要先遍历到目标位置,所以随机插入时ArrayList未必慢。"
标准答案:HashSet底层就是HashMap——元素存为HashMap的key,value是一个固定的Object常量PRESENT。add()调HashMap.put(),重复key直接覆盖value,所以不报错也不重复。
// HashSet源码本质
public boolean add(E e) {
return map.put(e, PRESENT) == null; // HashMap的key不重复
}
标准答案:TreeMap底层是红黑树,key按自然顺序或Comparator排序,put/get/remove都是O(logn)。HashMap无序O(1)。需要排序遍历时用TreeMap。
关键点:TreeMap的key必须实现Comparable或传入Comparator,否则put时报ClassCastException。
标准答案:
• JDK 1.7扩容时头插法,多线程并发resize可能形成环形链表,get时死循环——CPU直接100%
• JDK 1.8改为尾插法解决了死循环,但仍存在数据覆盖问题:两个线程同时put,可能一个被覆盖。
结论:"多线程用ConcurrentHashMap,不要用HashMap+外部同步。"
标准答案:
• synchronizedMap:所有方法加synchronized对象锁,读写全互斥,并发低
• ConcurrentHashMap:JDK 1.8用CAS+synchronized桶级锁,读不阻塞,写锁粒度细,并发高
一句话:"除了JDK 1.5以下版本兼容,都直接用ConcurrentHashMap。"
标准答案:用Iterator.remove(),不要用集合自身的remove()。增强for循环本质也是Iterator,但直接调list.remove()会触发ConcurrentModificationException(modCount != expectedModCount)。
// ✅ 正确 Iteratorit = list.iterator(); while (it.hasNext()) { if (it.next().equals("target")) it.remove(); } // ❌ 错误——ConcurrentModificationException for (String s : list) { if (s.equals("target")) list.remove(s); }
标准答案:
• Comparable:内部比较,类实现compareTo(),自然排序,侵入式修改
• Comparator:外部比较,独立写compare(),定制排序,非侵入,更灵活
场景:"Collections.sort(list)用Comparable;Collections.sort(list, new MyComparator())用Comparator。Stream.sorted(Comparator.comparing())是Comparator的现代用法。"
| 问 | 答 |
|---|---|
| HashMap 1.7 vs 1.8 | 头插→尾插,无红黑树→有红黑树,resize重hash→高低位分流 |
| ConcurrentHashMap 1.7 vs 1.8 | 分段锁Segment→CAS+synchronized桶锁,扩容单线程→多线程并发迁移 |
| HashSet去重原理 | 底层HashMap,元素当key,PRESENT当value |
| 链表转红黑树条件 | 链表长度≥8 且 数组长度≥64;退化为≤6 |
| HashMap扩容倍数 | 容量×2,阈值×2 |
| ArrayList扩容倍数 | 1.5倍(oldCapacity + oldCapacity>>1) |
这个页面帮到了你?扫码打赏,支持更多Java面试干货~