Java:整数等于= =


152

从Java 1.5中,你几乎可以互换Integer使用int在许多情况下。

但是,我发现代码中存在潜在的缺陷,这让我有些惊讶。

如下代码:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

值相等时,似乎错误地设置了不匹配,尽管我无法确定在什么情况下。我在Eclipse中设置了一个断点,发现Integer值都为137,并且检查了布尔表达式,并说它是假的,但是当我越过它时,它会将不匹配设置为true。

将条件更改为:

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

解决了问题。

谁能阐明为什么会这样?到目前为止,我只在自己的PC上的本地主机上看到了该行为。在这种情况下,代码成功地进行了约20次比较,但失败了2次。该问题始终可再现。

如果这是一个普遍的问题,那应该会在我们的其他环境(开发和测试)上引起错误,但是到目前为止,在执行了数百个测试代码片段之后,没有人报告过该问题。

==比较两个Integer值是否仍然不合法?

除了以下所有出色的答案之外,下面的stackoverflow链接还有很多其他信息。它实际上会回答我原来的问题,但是因为我没有在问题中提及自动装箱,所以它没有出现在所选建议中:

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

Answers:


238

JVM正在缓存Integer值。==仅适用于-128至127之间的数字 http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


1
谢谢,这当然可以解释为什么137失败了!它也回答了我的问题,为什么这不是一个普遍的问题,在我将要遇到的95%的情况下,该值将低于127。现在就可以抓住这个问题,尽管对于5%的情况不是这样。
杰里米·古德尔

1
有趣的旁注:直到几个星期前,cdiCt和cdsCt都是int,所以还可以,但是我必须将它们设置为Integers,以检查以不同方式处理的空情况……
Jeremy Goodell

3
@Jeremy是的,这是一个相当晦涩的问题,但是作为一般规则,您对对象使用.equals(),对于基元使用==。您不能依靠自动拆箱进行相等性测试。
亚当(2010年

1
大声笑,然后复选标记给您!看起来Colin已经拥有了足够多的分数。
杰里米·古德尔

2
注意,new Integer(1)!= new Integer(1)也是如此。new总是返回一个新地址。自动装箱使用缓存的版本。其他返回Integer的方法(不更新它们)也可能返回缓存的值。
比尔K

77

你不能Integer用一个简单的比较两个==对象作为对象因此大多数情况下引用都不相同。

有一个窍门, Integer在-128到127之间,引用将与自动装箱使用的引用相同,Integer.valueOf()后者会缓存小整数。

如果要装箱的值p是true,false,一个字节,\ u0000到\ u007f范围内的char或-128和127之间的整数或短数,则令r1和r2为任何两次装箱转换的结果的p。r1 == r2总是这样。


资源:

在同一主题上:


1
是JLS的保证还是Oracle JVM的保证?
托尔比约恩Ravn的安徒生

引述的部分是从JLS,所以它是一个从JLS的保证
科林·赫伯特

回复:保证。我仍然不会太依赖它。 new Integer(1) == new Integer(1)仍然是错误的。
Thilo 2015年

@Thilo new ... == new ...始终是false
MC Emperor

2
@Thilo是的,equals()在处理对象时始终使用。这应该是学习Java时应了解的第一件事。顺便说一下,我猜想的构造函数Integer是私有的,即实例总是通过该valueOf()方法创建的。但是我看到构造函数是公共的。
MC Emperor

5

问题是您的两个Integer对象就是那个对象。它们不匹配,因为您正在比较两个对象引用,而不是其中的值。显然,.equals它被重写以提供值比较,而不是对象引用比较。


很好的回答,但它没有解释为什么它只是没有为137.
杰里米·古德尔

4

Integer引用参考,也就是说,在比较参考时,您要比较的是它们指向的是同一对象,而不是值。因此,您遇到的问题。它与普通int类型一起使用时效果很好的原因是,它取消了包含的值的装箱Integer

我可以补充一点,如果您正在做自己在做的事情,为什么if要从语句开始?

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

4

“ ==”总是比较值的存储位置或对象引用。equals方法始终比较这些值。但是equals也间接使用“ ==”运算符比较值。

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

请参阅链接以获取其他信息


0

除了这些给出的好答案之外,我学到的是:

除非您打算通过引用将它们进行比较,否则请勿将它们与==进行比较。


0

为了确保使用的正确性,==您可以Integer在进行==比较之前将比较值之一开箱,例如:

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

第二个将自动开箱(当然,您必须先检查nulls)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.