Java集合存储对象引用,要求对象正确重写equals和hashCode才能支持查找、去重等操作;String已实现,自定义类常因未重写导致contains失败、Set重复、HashMap取值为null;用可变对象作Map key且修改影响hash的字段会导致失效;ArrayList非线程安全,多线程下可能抛ConcurrentModificationException或数据丢失。

Java集合完全适合存放对象,但必须明确:集合存的是对象引用,不是对象本身;且对象需满足特定契约(如 equals/hashCode 一致性)才能正确工作。
为什么 ArrayList\ 能存字符串,但自定义类常出问题?
根本原因在于集合内部依赖对象的 equals 和 hashCode 行为做查找、去重、哈希定位。String 已重写这两个方法,而多数自定义类没有。
常见错误现象:
-
list.contains(new Person("Alice", 30))返回false,即使列表里有同名同龄对象 -
Set中出现逻辑重复的元素 -
HashMap无法通过新构造的等价Person取值
解决路径:
立即学习“Java免费学习笔记(深入)”;
- 若用
ArrayList/LinkedList做纯容器(不调用contains/remove),可不重写 —— 但这是妥协,非设计推荐 - 若参与查找、去重、哈希操作,必须重写
equals和hashCode,且二者逻辑一致(例如都基于id字段) - Lombok 用户可直接加
@EqualsAndHashCode注解,但需确认字段选择合理(避免含可变字段)
Map 的 key 用对象时,哪些场景会“突然失效”?
最典型陷阱:把可变对象(mutable object)用作 HashMap 的 key,且后续修改了影响 hashCode 的字段。
例如:
Mapmap = new HashMap<>(); Person p = new Person("Bob", 25); map.put(p, "Engineer"); p.setAge(26); // 修改后 hashCode 改变 map.get(p); // 返回 null —— 因为哈希桶位置已错位,原值找不到了
安全做法:
- Key 类应设为不可变(immutable):字段
final,不提供 setter,构造后状态固定 - 若必须用可变对象,确保其作为 key 期间绝不修改影响
hashCode/equals的字段 - 优先考虑用业务主键(如
Long id)或不可变包装类(如UUID、String)作 key
并发环境下,直接用 ArrayList 存对象会崩吗?
会,但不是“立刻崩”,而是出现 ConcurrentModificationException 或数据丢失/脏读——取决于操作组合。
典型高危场景:
- 多线程同时调用
list.add():可能抛ArrayIndexOutOfBoundsException或静默丢数据(因扩容和元素赋值非原子) - 一个线程遍历
for (Person p : list),另一个调用list.remove():几乎必抛ConcurrentModificationException
对应方案:
- 读多写少 → 用
Collections.synchronizedList(new ArrayList()),但注意迭代仍需手动同步 - 高并发读写 → 直接换
CopyOnWriteArrayList(适合读远多于写的场景)或ConcurrentLinkedQueue(无界、弱一致性) - 需要线程安全 + 高性能 + 近实时一致性 → 考虑
java.util.concurrent.ConcurrentHashMap替代 List+Map 混合结构
对象放进集合不难,难的是理解集合底层如何使用对象的契约;最容易被忽略的,是把“能编译通过”当成“能正确运行”——尤其在并发、哈希、序列化等交叉场景下。










