为啥 == 与 Integer.valueOf(String) 的比较对 127 和 128 给出不同的结果?
Posted
技术标签:
【中文标题】为啥 == 与 Integer.valueOf(String) 的比较对 127 和 128 给出不同的结果?【英文标题】:Why do == comparisons with Integer.valueOf(String) give different results for 127 and 128?为什么 == 与 Integer.valueOf(String) 的比较对 127 和 128 给出不同的结果? 【发布时间】:2014-01-19 13:19:06 【问题描述】:我不知道为什么这些代码行返回不同的值:
System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));
输出是:
true
false
true
为什么第一个返回true
,第二个返回false
? 127
和 128
之间有什么我不知道的不同之处吗? (我当然知道127
128。)
还有,为什么第三个返回true
?
我已阅读答案of this question,但我仍然不明白它如何返回true
,以及为什么第二行中的代码返回false
。
【问题讨论】:
整数是一个对象;如果要比较是否相等,请使用.equals()
,否则所有赌注都关闭。
@KarlDamgaardAsmussen 实际上在这里我真的很想测试它们是否是对同一个对象的引用,起初我不明白为什么 127 128 返回不同的结果。
@DnR 如果 Java 是一种具有标准化规范的语言,我认为它会让这些事情由实现甚至强制执行未定义的行为。
@jszumski:this 的问题不仅仅是缓存部分。此外,链接的答案充其量是不完整的 - 它并没有详细说明缓存的内容和原因。
关于本次讨论的进一步跟进,请参考this meta post。
【参考方案1】:
这里有一个显着的区别。
valueOf
正在返回一个 Integer
对象,该对象的值可能缓存在 -128 和 127 之间。这就是为什么第一个值返回 true
- 它已缓存 - 而第二个值返回 false
- 128不是缓存值,因此您将获得两个单独的 Integer
实例。
请务必注意,您正在将引用与 Integer#valueOf
进行比较,如果您正在比较的值大于缓存支持的值,它将不 > 评估为true
,即使解析的值是等价的(例如:Integer.valueOf(128) == Integer.valueOf(128)
)。您必须改用equals()
。
parseInt
正在返回一个原语int
。这就是为什么第三个值返回true
- 128 == 128
被评估,当然是true
。
现在,恰好产生了第三个结果true
:
An unboxing conversion occurs 关于您正在使用的等价运算符和您拥有的数据类型 - 即 int
和 Integer
。当然,您会从右侧的valueOf
获得Integer
。
转换后,您将比较两个原始 int
值。比较会按照您对原语的预期进行,因此您最终会比较 128
和 128
。
【讨论】:
@user3152527:有相当大的区别 - 一个被视为对象,这意味着您可以调用方法并在抽象数据结构中与之交互,例如List
。另一个是原始值,它只是一个原始值。
@user3152527 你问了一个很好的问题(在最坏的情况下也不是一个愚蠢的问题)。但是您已将其修复为使用 .equals,对吧?
啊,提问者似乎没有理解 Java 中的一个基本事实:当使用“==”比较两个对象时,您正在测试它们是否是对同一对象的引用。使用“equals()”时,您正在测试它们是否具有相同的值。您不能使用“等于”来比较基元。
@Jay 不,我明白这一点。但一开始让我感到困惑的是为什么第一个返回 true 而第二个返回 false 使用相同的比较方法 ==
。无论如何,现在清楚了。
尼特:不仅仅是整数“可能”被缓存在 -128 和 127 之间。根据JLS 5.1.7,它必须是。它可能在该范围之外被缓存,但不一定要(而且通常不是)。【参考方案2】:
Integer
类有一个静态缓存,它存储 256 个特殊的 Integer
对象 - 一个对应于 -128 和 127 之间的每个值。考虑到这一点,考虑这三个之间的区别。
new Integer(123);
这(显然)创建了一个全新的 Integer
对象。
Integer.parseInt("123");
这会在解析String
后返回一个int
原始值。
Integer.valueOf("123");
这比其他的更复杂。它首先解析String
。然后,如果该值介于 -128 和 127 之间,则从静态缓存中返回相应的对象。如果该值超出此范围,则调用new Integer()
并传入该值,从而获得一个新对象。
现在,考虑问题中的三个表达式。
Integer.valueOf("127")==Integer.valueOf("127");
这将返回 true,因为值为 127 的 Integer
从静态缓存中检索了两次,并与自身进行了比较。只涉及一个Integer
对象,因此返回true
。
Integer.valueOf("128")==Integer.valueOf("128");
这会返回false
,因为128 不在静态缓存中。因此,为等式的每一侧都创建了一个新的Integer
。由于有两个不同的Integer
对象,并且对象的==
仅在双方都是完全相同的对象时才返回true
,这将是false
。
Integer.parseInt("128")==Integer.valueOf("128");
这是比较左侧的原始 int
值 128 和右侧新创建的 Integer
对象。但是因为将int
与Integer
进行比较是没有意义的,Java 会在进行比较之前自动取消对Integer
的装箱;所以你最终会将int
与int
进行比较。由于基元 128 等于自身,因此返回 true
。
【讨论】:
最好的解释在这里。【参考方案3】:注意这些方法的返回值。 valueOf 方法返回一个 Integer 实例:
public static Integer valueOf(int i)
parseInt 方法返回整数值(原始类型):
public static int parseInt(String s) throws NumberFormatException
比较说明:
为了节省内存,两个实例 包装对象,当它们的时候总是 == 原始值是相同的:
布尔值 字节 从 \u0000 到 \u007f 的字符(7f 是十进制的 127) 从 -128 到 127 的短整数和整数当 == 用于比较原语和包装器时,包装器将是 展开后,比较将是原始到原始的。
在您的情况下(根据上述规则):
Integer.valueOf("127")==Integer.valueOf("127")
此表达式比较对同一对象的引用,因为它包含介于 -128 和 127 之间的整数值,因此它返回 true
。
Integer.valueOf("128")==Integer.valueOf("128")
此表达式比较对不同对象的引用,因为它们包含的整数值不在 中,因此它返回 false
。
Integer.parseInt("128")==Integer.valueOf("128")
此表达式比较原始值(左侧)和对象引用(右侧)
所以右手边将被展开,他的原始类型将与左侧进行比较,因此它返回true
。
【讨论】:
类似问题:***.com/questions/9824053/… 能否提供报价来源的网址?【参考方案4】:整数对象缓存在 256 个整数中的 -128 和 127 之间
您不应将对象引用与 == 或 != 进行比较。您应该改用 .equals(..),或者更好 - 使用原始 int 而不是 Integer。
parseInt:将字符串参数解析为有符号十进制整数。字符串中的字符必须都是十进制数字,除了第一个字符可以是 ASCII 减号“-”(“\u002D”)表示负值。返回结果整数值,就好像参数和基数 10 作为 parseInt(java.lang.String, int) 方法的参数一样。
valueOf 当使用第二个参数给出的基数进行解析时,返回一个 Integer 对象,该对象包含从指定 String 中提取的值。第一个参数被解释为表示由第二个参数指定的基数中的有符号整数,就像将参数提供给 parseInt(java.lang.String, int) 方法一样。结果是一个表示字符串指定的整数值的 Integer 对象。
相当于
new Integer(Integer.parseInt(s, radix))
radix - 用于解释 s 的基数
所以如果你等于Integer.valueOf()
之间的整数
-128 到 127 在你的情况下返回 true
对于lesser than
-128 和greater than
127 它给出false
【讨论】:
【参考方案5】:为了补充给定的答案,还请注意以下几点:
public class Test
public static void main(String... args)
Integer a = new Integer(129);
Integer b = new Integer(129);
System.out.println(a == b);
此代码还将打印:false
正如用户 Jay 在评论中声明的已接受答案,在对象上使用运算符 ==
时必须小心,在这里您要检查两个引用是否相同,而不是,因为它们是不同的对象,尽管它们代表相同的值。要比较对象,您应该改用equals
方法:
Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));
这将打印:true
你可能会问,但是为什么第一行打印true
?。查看Integer.valueOf
方法的源码,可以看到如下:
public static Integer valueOf(String s) throws NumberFormatException
return Integer.valueOf(parseInt(s, 10));
public static Integer valueOf(int i)
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
如果参数是IntegerCache.low
(默认为-128)和IntegerCache.high
(在运行时计算,最小值为127)之间的整数,则返回一个预分配(缓存)的对象。因此,当您使用 127 作为参数时,您将获得对同一缓存对象的两个引用,并在比较引用时获得 true
。
【讨论】:
以上是关于为啥 == 与 Integer.valueOf(String) 的比较对 127 和 128 给出不同的结果?的主要内容,如果未能解决你的问题,请参考以下文章
Integer.valueof(String s)和Integer.parseInt(String s)的具体区别是什么?
Integer.parseInt(String s) 和 Integer.valueOf(String s) 的区别
Java面试题之Integer.valueOf(String s);采用了什么设计模式