Java集合常见陷阱包括:遍历时直接删除元素引发ConcurrentModificationException,应使用迭代器remove()或removeIf();HashMap用可变对象作key导致查找失败,需保证hashCode/equals一致性且优先用不可变对象;ArrayList频繁add引发多次扩容,应预设初始容量;误用原始类型集合丢失泛型安全,须始终声明泛型类型。

Java集合用得不熟,很容易踩坑——不是空指针报错,就是遍历时修改出 ConcurrentModificationException,再或者用错类型导致性能暴跌。关键不在“会不会用”,而在“有没有意识到这些隐性陷阱”。
遍历中直接删除元素引发异常
这是最经典也最容易复现的错误:用 for-each 或 Iterator 遍历时,调用集合自身的 remove() 方法(如 list.remove(obj)),会触发 ConcurrentModificationException。因为迭代器内部有 modCount 校验机制,而集合直接修改会改变结构但不通知迭代器。
- ✅ 正确做法:用迭代器自己的 remove() 方法
- ✅ 替代方案:收集待删元素,遍历完再批量 removeAll()
- ✅ Java 8+ 推荐:用 removeIf()(如 list.removeIf(x -> x == null)
HashMap 用可变对象作 key 导致查不到
如果把自定义对象(比如 Person)当 HashMap 的 key,但没重写 hashCode() 和 equals(),或者重写了却让 hash 值随对象字段变化(比如用含可变字段的计算逻辑),就会出现 put 进去后 get 不回来的情况——因为 hash 槽变了,但对象还留在旧位置。
- ✅ key 类必须保证:equals 相等时 hashCode 一定相等
- ✅ 尽量用不可变对象(如 String、Integer)作 key
- ✅ 若必须用自定义类,字段声明为 final,且 hashCode/equals 仅基于 final 字段生成
ArrayList 频繁 add() 引发多次扩容
ArrayList 底层是数组,初始容量通常为 10。每次扩容要新建数组 + 复制元素,开销不小。如果事先知道大概数量(比如读取 5000 行文件),却不指定初始容量,可能触发 5~6 次扩容。
立即学习“Java免费学习笔记(深入)”;
- ✅ 构造时预估大小:new ArrayList(expectedSize)
- ✅ 注意:传入的不是“最小容量”,而是“期望容量”,框架会向上取最近的 2 的幂(如传 100 → 实际分配 128)
- ✅ 如果 size 固定且不增删,考虑用 Arrays.asList() 或 Collections.unmodifiableList()
误用原始类型集合(Raw Type)丢失泛型安全
写成 List list = new ArrayList(); 看似能编译,但等于主动关闭了编译期类型检查。往里加 String、Integer、File 都不会报错,取出来却要强转,运行时 ClassCastException 风险陡增,IDE 也无法提示问题。
- ✅ 始终使用带泛型的声明:List
list = new ArrayList(); - ✅ 构造器可用菱形语法(),避免重复写泛型
- ✅ 第三方库返回 raw type?立即包装成泛型视图,或用 @SuppressWarnings("unchecked") 显式标注并加注释说明
基本上就这些——不复杂,但容易忽略。避开它们,集合代码就稳了一大半。










