为啥 == 与 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,第二个返回false127128 之间有什么我不知道的不同之处吗? (我当然知道127128。)

还有,为什么第三个返回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 关于您正在使用的等价运算符和您拥有的数据类型 - 即 intInteger。当然,您会从右侧的valueOf 获得Integer

转换后,您将比较两个原始 int 值。比较会按照您对原语的预期进行,因此您最终会比较 128128

【讨论】:

@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 对象。但是因为将intInteger 进行比较是没有意义的,Java 会在进行比较之前自动取消对Integer 的装箱;所以你最终会将intint 进行比较。由于基元 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);采用了什么设计模式

Integer.valueOf() 与 Integer.parseInt() [重复]

为啥我不能用相同的参数类型组合两个不同的函数?

JAVA探讨 Java 中 valueOf 和 parseInt 的区别