Java: Integer equals vs. ==

199
自 Java 1.5 开始,你在许多情况下可以相互替换`Integer`和`int`。
然而,我在我的代码中发现了一个潜在的缺陷,这让我感到有点惊讶。
下面是代码:
Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

看起来在某些情况下,即使值相等,它也似乎会错误地设置为不匹配。我在Eclipse中设置了断点,发现Integer的值都为137,我检查了布尔表达式,它说是false,但是当我单步执行时,它却将mismatch设置为true。

将条件语句改为:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

问题已解决。

有人能够解释一下为什么会出现这种情况吗?到目前为止,我只在我的个人PC上的本地主机上看到了这种行为。在这种特殊情况下,代码成功通过了大约20个比较,但在2个比较后失败了。该问题具有一致的可重现性。

如果这是普遍存在的问题,那么它应该会在我们的其他环境(dev和test)上引发错误,但到目前为止,在执行此代码片段的数百个测试中,没有人报告出现问题。

对于比较两个Integer值,仍然不能使用==吗?

除了下面所有很好的答案之外,以下stackoverflow链接提供了相当多的额外信息。实际上,它本来可以回答我最初的问题,但因为我没有在问题中提及装箱,所以它没有出现在选定建议中:

为什么编译器/JVM不能使自动装箱“正常工作”?

7个回答

312

1
谢谢,这解释了为什么137会失败!它也回答了我关于为什么这不是一个普遍问题的问题,在我遇到的95%的情况下,该值将低于127。现在对于那5%的情况来说,能够及时发现这个问题很好。 - Jeremy Goodell
1
有趣的一面是:直到几周前,cdiCt和cdsCt都是int类型,所以这很好,但我不得不将它们变成Integers类型,以便检查处理方式不同的空值情况... - Jeremy Goodell
3
是的,这是一个相当晦涩的问题,但一般规则是对于对象使用.equals()方法,对于基本数据类型使用==运算符。您不能依赖自动拆箱来进行相等性测试。 - Adam
1
哈哈,那就给你打个勾吧!看起来 Colin 已经有足够的分数了。 - Jeremy Goodell
2
请注意,new Integer(1)!= new Integer(1)。 new始终返回新地址。 自动装箱使用缓存版本。 其他返回Integer的方法(不使用new)可能也返回缓存值。 - Bill K
显示剩余2条评论

92
你不能简单地使用==来比较两个Integer,因为它们是对象,所以大多数情况下引用不会相同。
有一个技巧,对于介于-128和127之间的Integer,引用将与自动装箱使用的Integer.valueOf()相同,它缓存小整数。

如果被封装的值p为true,false,一个byte,一个范围在\u0000到\u007f之间的char,或者一个介于-128和127之间的int或short数字,则让r1和r2分别表示p的任意两个装箱转换的结果。对于任何情况都有 r1 == r2。


资源:

同一主题:


1
这个保证是来自JLS还是仅适用于Oracle JVM? - Thorbjørn Ravn Andersen
2
回复:保证。我仍然不会太依赖它。new Integer(1) == new Integer(1) 仍然是假的。 - Thilo
2
@Thilo new ... == new ... 总是 false - MC Emperor
4
@Thilo 确实,处理对象时应始终使用 equals()。这应该是学习 Java 时应首先了解的内容之一。顺便说一下,我本以为 Integer 的构造函数是私有的,即实例始终通过 valueOf() 方法创建。但我发现构造函数是公共的。 - MC Emperor
@MCEmperor:构造函数不是私有的可能是早期的疏忽。valueOf和缓存是后来添加的。 - Thilo
显示剩余3条评论

6

"==" 操作符总是比较值的内存位置或对象引用。equals 方法总是比较值。但是 equals 方法也间接使用 "==" 操作符来比较值。

Integer 使用 Integer 缓存来存储从 -128 到 +127 的值。如果使用 == 操作符检查任何介于 -128 到 127 之间的值,则返回 true。对于其他值,它返回 false。

有关一些额外信息,请参阅 链接


5
问题在于你的两个整数对象只是对象,它们不匹配是因为你比较的是两个对象引用,而不是其中的值。很明显,.equals 被重写以提供值比较,而不是对象引用比较。

好的回答,但它没有解释为什么只有137会失败。 - Jeremy Goodell

5

Integer指的是引用,也就是说当比较引用时,比较的是它们是否指向相同的对象,而不是值。这就是你所看到的问题所在。使用普通的int类型可以很好地工作的原因是它会拆箱Integer中包含的值。

我可以补充一下,如果你正在做你要做的事情,为什么还要有if语句呢?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );

3
除了这些很好的答案,我所学到的是:

永远不要使用==来比较对象,除非你想通过它们的引用进行比较。


1
那是因为“==”更或多或少地比较堆栈上的值。对于基元类型,它们是它们的值,而对于对象则是它们的引用(哈希码)。".equals"比较相应类覆盖的方法中定义的任何内容。请注意,如果没有覆盖,默认情况下“Object”的“.equals”执行以下操作:“return (this == obj)”。 - Terran

2

为了正确使用==,您可以在进行==比较之前,将要比较的Integer值中的一个取消装箱,例如:

if ( firstInteger.intValue() == secondInteger ) {..

第二个将会自动取消封箱(当然,您必须先检查是否为null)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接