java集合高频考点本质是底层机制与边界行为的真题检查清单:如hashmap多线程死循环(jdk7头插法成环)、concurrenthashmap非强一致、arraylist重载陷阱、hashset无序性、arrays.aslist()不可变等。

Java 集合框架没有“标准答案表”,所谓 50 个高频考点对照,本质是把底层机制、边界行为和面试常挖的坑,用真题反推出来的检查清单——不是背点就能过,而是得知道哪个 HashMap 的扩容阈值算错会直接导致死循环,哪个 ArrayList 的 remove() 调用方式会让索引错位。
为什么 HashMap 在多线程下可能死循环?
JDK 7 的 HashMap 扩容时用头插法迁移链表,多个线程同时触发扩容,可能让两个节点互相指向,形成环形链表。JDK 8 改为尾插 + 红黑树,但依然不保证线程安全——它只解决死循环,不解决数据覆盖或丢失。
- 别拿
ConcurrentHashMap当HashMap的线程安全平替:它的size()是估算值,containsKey()和get()不是原子组合操作 - 真要线程安全且强一致性,用
Collections.synchronizedMap(new HashMap()),但注意所有遍历必须手动加synchronized块 - JDK 8 后如果 key 是可变对象,且在
HashMap中被修改了hashCode()相关字段,get()就再也找不到它——不是 bug,是 contract 违反
ArrayList 的 remove(int) 和 remove(Object) 怎么总删错?
这是重载陷阱:remove(int) 删下标,remove(Object) 删值。传一个 Integer,编译器可能自动装箱后走错重载,尤其当集合里存的是数字类型时。
- 明确意图:删索引就用
list.remove(2);删值就写成list.remove((Object) 2)或list.remove(Integer.valueOf(2)) -
ArrayList删除中间元素性能差,因为要数组拷贝;高频删除建议换LinkedList,但注意它的get(int)是 O(n),别在循环里乱调 - 用增强 for 循环遍历时调
remove(),必抛ConcurrentModificationException——得用Iterator.remove()
HashSet 为什么查得快,却不能保证顺序?
HashSet 底层是 HashMap,key 存元素、value 存 PRESENT 占位符。哈希分布决定访问效率,但插入顺序完全由 hash 值和桶位置决定,和你 add 的先后无关。
- 需要插入顺序 → 用
LinkedHashSet,它用双向链表维护 insertion-order,开销略大但可控 - 需要排序 → 直接上
TreeSet,但注意它要求元素实现Comparable或传Comparator,且null值在自然排序下会 NPE -
HashSet的add()返回boolean:true 表示新增成功,false 表示已存在——这个返回值常被忽略,却是去重逻辑的关键判断点
Arrays.asList() 返回的 List 为什么不能增删?
它返回的是 Arrays 类里的静态内部类 ArrayList(注意不是 java.util.ArrayList),这个类只实现了 set()、get()、contains(),但 add()、remove() 直接抛 UnsupportedOperationException。
- 要可变,必须 new 一遍:
new ArrayList(Arrays.asList(...)) - 它对原始数组是“视图”:改数组内容,List 看得见;改 List 的
set(),数组也同步变——这点常被当成深拷贝误用 - 泛型擦除后,
Arrays.asList(null)会生成含一个null元素的列表;但Arrays.asList()无参调用返回空列表,不是 null
集合最麻烦的从来不是 API 怎么写,而是你以为的“安全”“有序”“一致”,其实都绑定在具体实现类、JDK 版本、甚至你传进去的对象是否可变上。稍微跳一步,比如把 HashMap 换成 ConcurrentHashMap,或者把 ArrayList 传给一个期望 List 接口的方法,就可能暴露隐性依赖——这些地方,光背考点没用,得动手改两行代码,看它报什么错、返回什么值,才真正算过关。










