==比较内存地址,equals()比较内容;高并发下同值对象地址不同,用==易失准,应依场景选equals()、基础类型比较或Objects.equals()。

在集合数据高并发匹配中直接用 == 比较,本质是拿对象的内存地址做判断,而不是比内容——这在多线程、多来源、多构造路径的场景下极大概率失准,形成“地址陷阱”。
核心问题:两个逻辑上相等的值,可能被创建为不同对象
Java 中只有部分类重写了 equals(),而 == 始终只看引用是否指向同一块内存。高并发环境下,这种“地址即身份”的假设极易崩塌:
- 多个线程分别从 JSON、数据库、RPC 响应中解析出相同的数值(如
200),但各自 new 出独立的Integer实例 - 同一个浮点字段经不同反序列化库(Jackson vs Gson)、不同 JVM 版本或不同优化路径处理,生成的
Double对象地址必然不同 - 使用
new Integer(200)或Double.valueOf("0.95")等显式构造方式,绕过了缓存机制,哪怕值相同也绝不复用对象
这时用 == 判断,哪怕 a.equals(b) 为 true,a == b 也几乎总是 false。
典型失真场景举例
-
ConcurrentHashMap作为匹配容器时,若 key 是未重写hashCode()/equals()的自定义对象,或误用==替代equals()做条件过滤,会导致查不到已存在的匹配项 - 并发批量加载用户标签,多个线程各自构建
Tag对象(含id: 101,name: "VIP"),用==去 dedup,结果每个都当成新标签插入 - 文件元数据匹配中,用
file.getSize() == 1024L判断大小,而getSize()返回的是每次 new 的Long包装类 → 永远不成立
真正安全的替代方式
- 字符串比较:一律用
Objects.equals(str1, str2)或str1.equals(str2)(注意判空) - 数值包装类比较:优先用基础类型(
int a = obj1.intValue(); int b = obj2.intValue(); a == b),或Objects.equals() - 自定义对象作 key 或匹配依据:必须同时重写
equals()和hashCode(),且逻辑一致;禁止依赖== - 高并发集合操作中,避免把
==当作业务相等性判断依据,它只适合判断“是不是同一个实例”这种极窄语义
本质上,== 是 JVM 层的指针比较,不是业务层的语义比较。高并发放大了对象创建路径的多样性,让“同值不同址”成为常态——把 == 当 equals() 用,就是主动跳进地址陷阱。


















