应按场景分层选择比较方式:基本类型间用==,包装类间用equals,高频排序优先转基本类型,null安全用Objects.equals;语义正确性与JVM可预测性比字节码精简更重要。

包装类比较方法与基本类型比较符在字节码层面行为不同,选型不能只看表面写法,得结合实际运行时的指令开销、对象生命周期和JVM优化能力综合判断。压测前先明确目标:是比单次操作延迟?还是高并发下GC压力?或是长期运行的吞吐稳定性?
看字节码:== 和 equals 的真实执行路径不同
对 Integer a = 100, b = 100; 执行 a == b:
- JVM自动拆箱为
a.intValue() == b.intValue(),最终生成if_icmpne指令——纯整数寄存器比较,无对象访问开销 - 而
a.equals(b)编译后调用Integer.equals(Object),内部仍会拆箱再比值,但多出方法分派、空指针检查、类型判断等字节码(如invokevirtual、checkcast) - 若其中一个是
null,==仍安全返回false;equals直接抛NullPointerException,触发异常处理机制,代价更高
缓存区间(-128 ~ 127)让 == 表面“快”,但不可靠
当数值落在缓存范围内,Integer a = 100; Integer b = 100; 的 == 确实比较地址且命中同一对象,看似零开销。但:
- 一旦数值超出范围(如
200),==就退化为两个堆对象地址比较,结果恒为false,逻辑错误风险远大于性能收益 - 使用
new Integer(100)或反射创建对象,哪怕值在缓存内,==也必然失败——压测数据若混入这类构造方式,结果将严重失真 - JIT编译器对稳定模式的
equals调用可能内联优化,实际差距比字节码显示的小;但对不稳定的==场景,JIT无法做等价假设
压测选型建议:按场景分层决策
不推荐统一用某一种写法,应按数据来源和语义确定:
-
基本类型间或基本类型 vs 包装类:直接用
==。字节码简洁,JVM保障语义正确,压测中延迟最低且稳定 -
包装类之间判等(如集合查重、DTO字段校验):强制用
equals。避免缓存陷阱,语义清晰,现代JVM对其优化充分,压测QPS差异通常<3% -
高频数值排序或范围判断:优先转基本类型再比,例如
obj1 != null && obj2 != null && obj1.intValue() > obj2.intValue()。跳过 equals 的空检查和类型转换,字节码更紧凑 -
涉及 null 安全的通用工具方法:用
Objects.equals(a, b)。它内部已对 null 做短路处理,字节码虽略多,但避免了 NPE 导致的线程中断,压测长稳性更好
本质上,这不是字节码指令数量的比拼,而是语义正确性、JVM可预测性与工程鲁棒性的平衡。盲目追求 == 的字节码精简,在真实业务中反而容易引发偶发故障,压测指标再好看也难落地。











