应统一使用equals()而非==进行集合匹配,因==比较地址易致漏判;包装类缓存使-128~127内==偶发成立但不可靠;反序列化等必产新对象;自定义类须重写equals()与hashcode()。

因为在集合匹配中,== 比较的是对象地址,不是内容值。两个逻辑上完全相同的元素,只要不是同一个实例,== 就返回 false——这在集合查找、去重、判断存在性时,会直接导致漏判或误判。
包装类缓存机制让人误判
Integer、Short 等包装类对 -128 到 127 范围内的值做了缓存,所以:
-
Integer a = 100; Integer b = 100;→a == b是 true(共享缓存对象) -
Integer c = 128; Integer d = 128;→c == d几乎总是 false(各自 new 出新对象)
同一段代码,输入数据稍一变化,行为就突变,极难调试。
反序列化和持久化必然生成新对象
集合里的元素常来自外部:JSON 解析、数据库查询、Redis 反序列化、RPC 响应……这些操作都会创建全新实例:
- Jackson 解析
{"id": 200}→ 得到一个 new Integer(200) - MyBatis 查出 id=200 的记录 → 又是一个 new Integer(200)
- 两者数值相同、类型相同,但内存地址不同 →
==判为 false
自定义对象默认不比较内容
如果集合里存的是自定义类(如 User、Order),而没重写 equals() 和 hashCode():
-
list.contains(user)内部调用的是user.equals(某元素) - 没重写时,走的是 Object 默认实现 —— 实际仍是
==地址比较 - 哪怕两个 User 的字段一模一样,只要不是同一个 new 出来的实例,就匹配不上
安全做法很简单
别纠结“什么时候能用 ==”,统一按语义选方法:
- 字符串比较:用
Objects.equals(s1, s2)(自动防 null) - 数值包装类:优先转基础类型再比,如
obj1.intValue() == obj2.intValue();或用Objects.equals() - 自定义对象作匹配依据:必须重写
equals()和hashCode(),且逻辑一致 - 集合自带方法(如
contains()、remove())已基于equals()实现,放心用











