为什么与Integer.valueOf(String)进行==比较会得出127和128的不同结果?


182

我不知道为什么这些代码行返回不同的值:

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?有什么不同,我不知道之间127128?(我当然知道127< 128。)

另外,为什么第三个返回true

我已经阅读了这个问题的答案,但是我仍然没有知道它如何返回true,以及为什么第二行中的代码返回false


6
整数是一个对象;如果您想比较是否相等,请使用.equals(),否则所有选择均不适用。
Karl

6
@KarlDamgaardAsmussen实际上,我真的想测试它们是否是对同一对象的引用,起初我不明白为什么127128返回不同的结果。
DnR 2014年

@DnR如果Java是具有标准化规范的语言,我认为它将使此类问题取决于实现甚至是强制性的未定义行为。
Karl Damgaard Asmussen 2014年

1
@jszumski:这个问题不仅限于缓存部分。此外,链接的答案充其量是不完整的-关于缓存的内容和原因,它还不够详细。
Makoto 2014年

1
有关此讨论的进一步跟进,请参阅此meta post
Jeroen Vannevel 2014年

Answers:


191

这里有一个惊人的区别。

valueOf正在返回一个Integer对象,该对象的值可能缓存在-128到127之间。这就是为什么第一个值返回true-它被缓存-第二个值返回-128 false不是缓存值,所以得到两个单独的Integer实例。

这是需要注意的重要,你是比较参考Integer#valueOf,如果您比较值比什么缓存支持的更大,这将计算为true,即使解析的值相等(案例: Integer.valueOf(128) == Integer.valueOf(128))。您必须equals()改为使用。

parseInt正在返回原始值int。这就是第三个值返回的原因true- 128 == 128被评估,当然是true

现在,恰好可以得出第三个结果true

  • 对于您正在使用的等价运算符以及您拥有的数据类型(即int和),发生拆箱转换Integer。当然,您是IntegervalueOf右侧获得的。

  • 转换后,您正在比较两个原始int值。就像您期望的那样,比较会发生,因此您结束了比较128128


2
@ user3152527:有很大的不同-一个被认为是对象,这意味着您可以调用方法并在诸如的抽象数据结构中与之交互List。另一个是原始值,仅是原始值。
Makoto 2014年

1
@ user3152527您提出了一个很好的问题(最糟糕的情况不是愚蠢的问题)。但是您已将其固定为使用.equals,对吗?
user2910265'1

3
嗯,似乎发问者不理解Java中的基本事实:当使用“ ==”比较两个对象时,您正在测试它们是否是对同一对象的引用。使用“ equals()”时,正在测试它们是否具有相同的值。您不能使用“等于”来比较基元。
2014年

3
@杰伊不,我明白。但是首先让我困惑的是为什么第一个使用相同的比较方法返回true,而第二个返回false ==。无论如何,现在就清楚了。
DnR 2014年

1
Nit:不仅可以在-128和127之间缓存Integer“可以”,而且还必须根据JLS 5.1.7进行缓存。它可能被缓存在该范围之外,但是不必(通常不是)被缓存。
yshavit

127

Integer类有一个静态缓存器,存储256个特殊的Integer对象-一个用于记-128和127.这之间的每一个值,考虑这三者之间的区别。

new Integer(123);

这(显然)是一个全新的Integer对象。

Integer.parseInt("123");

int解析后,将返回原始值String

Integer.valueOf("123");

这比其他更为复杂。它开始于解析String。然后,如果该值在-128到127之间,它将从静态缓存中返回相应的对象。如果该值超出此范围,则它将调用new Integer()并传入该值,以便获得一个新对象。

现在,考虑问题中的三个表达式。

Integer.valueOf("127")==Integer.valueOf("127");

这返回true,因为Integer从静态缓存中两次检索了值为127的值,并将其与自身进行比较。仅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


13

请注意从这些方法返回值。该的valueOf方法返回一个整数实例:

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之间的Integer值,因此返回true

Integer.valueOf("128")==Integer.valueOf("128")

此表达式比较对不同对象的引用,因为它们包含不在<-128,127>中的Integer值,因此它返回false

Integer.parseInt("128")==Integer.valueOf("128")

此表达式将原始值(左侧)与对对象的引用(右侧)进行比较,以便将右侧展开,并将其原始类型与左侧进行比较,以便返回true



您可以提供报价来源的网址吗?
Philzen

“ ...包装对象的两个实例,当它们的原始值相同时,总是== ...” -绝对错误。如果您创建两个具有相同值的包装器对象,则与相比,它们将不会返回true ==,因为它们是不同的对象。
达伍德·伊本·卡里姆

6

整数对象在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 than127它给出false


6

为了补充给定的答案,还请注意以下几点:

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在评论中所接受的答案所声明的那样,==在对象上使用operator时必须格外小心,在这里您要检查两个引用是否相同,这是不对的,因为它们是不同的对象,尽管它们表示对象。相同的值。要比较对象,应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这些引用的比较。

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.