==比较内存地址,反序列化等操作会生成新对象导致匹配失败;应统一用equals()或Objects.equals()进行值比较。

因为在集合数据持久化匹配中,双等号(==)比较的是对象在堆内存中的地址,而不是它们的业务内容。一旦对象经过序列化/反序列化、数据库读写、JSON 解析或跨线程传递,哪怕值完全相同,也大概率生成新实例——地址不同,== 就返回 false,导致本该匹配成功的记录被跳过。
常见触发场景
这些操作都会破坏引用一致性,但开发者常误以为“值一样就该 == 成立”:
- 从 MySQL 查询出 Integer 字段 → 得到 new Integer(128),不是缓存对象
- 用 Jackson 解析 JSON 数组里的数字 → 生成全新 Long 或 Integer 实例
- Redis 中存取对象后反序列化 → 原对象地址彻底丢失
- MyBatis 的 resultMap 映射结果 → 每次查询都新建实体类对象
包装类缓存机制加剧误导
Integer 在 -128 到 127 范围内会复用缓存对象,所以 Integer a = 100; Integer b = 100; 时 a == b 碰巧为 true;但一旦值是 128,a == b 就变成 false —— 同一套代码,在不同数据下行为突变,极难复现和定位。
集合匹配时的真实风险
比如用 List
- 若 ID 来自前端 JSON:
{"roleId": 128}→ 解析为 new Integer(128) - 若 ID 来自 DB 查询:MyBatis 返回的也是 new Integer(128)
- 两者数值相同、类型相同,但地址不同 →
list.contains(targetId)内部若误用 ==(或手动遍历用 == 比较),就会漏判
安全替代方案
统一使用语义明确、null 友好、不依赖内存布局的方式:
- 同类型比较:用
.equals(),如id1.equals(id2) - 可能为 null 的场景:用
Objects.equals(id1, id2),自动判空且不抛 NPE - 集合查找:确保集合元素重写了
equals和hashCode,并用contains()方法(它内部调用equals)

















