Why do == comparisons with Integer.valueOf(String) give different results for 127 and 128?
Asked Answered
F

5

187

I have no idea why these lines of code return different values:

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"));

The output is:

true
false
true

Why does the first one return true and the second one return false? Is there something different that I don't know between 127 and 128? (Of course I know that 127 < 128.)

Also, why does the third one return true?

I have read the answer of this question, but I still didn't get how it can return true, and why the code in second line returns false.

Frayda answered 2/1, 2014 at 5:49 Comment(7)
Integer is an object; if you want to compare for equality, use .equals(), otherwise all bets are off.Cordovan
@KarlDamgaardAsmussen Actually here I really want to test if they are references to the same object, and at first I don't get it why 127 128 return different result.Frayda
@Frayda if Java was a language with a standardized specification, I would think it let such matters up to implementation or even mandated undefined behaviour.Cordovan
@Makoto "The JVM is caching Integer values. == only works for numbers between -128 and 127" from the accepted answer, which explains why DnR was seeing this issue precisely at the 127-128 boundary.Cottonwood
@jszumski: There's more to this question than just the caching portion, though. Besides, the linked answer is incomplete at best - it doesn't quite go into detail as to what's cached and why.Anacoluthia
For further followup on this discussion, please refer to this meta post.Farinaceous
Nice little Java puzzler :) If I was in charge of evaluating developer applicants, this would be an awesome test. Luckily, I'm out of this horrid business :)Chromonema
A
193

There's a striking difference here.

valueOf is returning an Integer object, which may have its values cached between -128 and 127. This is why the first value returns true - it's cached - and the second value returns false - 128 isn't a cached value, so you're getting two separate Integer instances.

It is important to note that you are comparing references with Integer#valueOf, and if you are comparing a value that is larger than what the cache supports, it will not evaluate to true, even if the parsed values are equivalent (case in point: Integer.valueOf(128) == Integer.valueOf(128)). You must use equals() instead.

parseInt is returning a primitive int. This is why the third value returns true - 128 == 128 is evaluated, and is of course, true.

Now, a fair bit happens to make that third result true:

  • An unboxing conversion occurs with respect to the equivalence operator you're using and the datatypes you have - namely, int and Integer. You're getting an Integer from valueOf on the right hand side, of course.

  • After the conversion, you're comparing two primitive int values. Comparison happens just as you would expect it to with respect to primitives, so you wind up comparing 128 and 128.

Anacoluthia answered 2/1, 2014 at 5:52 Comment(6)
@user3152527: There's a sizable difference - one is considered an object, which means you can call methods and interact with it in abstract data structures, like List. The other is a primitive, which is just a raw value.Anacoluthia
@user3152527 You asked an excellent question (and not a dumb one at worst). But you've fixed it to use .equals, right?Fortieth
Ah, it appears that the questioner did not understand an underlying fact in Java: When using "==" to compare two objects, you are testing if they are references to the same object. When using "equals()", you are testing if they have the same value. You cannot use "equals" to compare primitives.Stoll
@Stoll no, I understand that. but the one that confusing me at first is why the first one return true and second one return false using the same comparison method == . anyway, its clear now.Frayda
Nit: it's not just that Integer "may" be cached between -128 and 127. It must be, according to JLS 5.1.7. It may be cached outside of that range, but doesn't have to be (and often isn't).Molybdous
@SharadHolani: The values in the cache are very much Integers. This is why you can use == on them since they're guaranteed to be the same instance as the ones in the cache.Anacoluthia
R
129

The Integer class has a static cache, that stores 256 special Integer objects - one for every value between -128 and 127. With that in mind, consider the difference between these three.

new Integer(123);

This (obviously) makes a brand new Integer object.

Integer.parseInt("123");

This returns an int primitive value after parsing the String.

Integer.valueOf("123");

This is more complex than the others. It starts off by parsing the String. Then, if the value is between -128 and 127, it returns the corresponding object from the static cache. If the value is outside of this range, then it invokes new Integer() and passes in the value, so that you get a new object.

Now, consider the three expressions in the question.

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

This returns true, because the Integer whose value is 127 is retrieved twice from the static cache, and compared to itself. There's only one Integer object involved, so this returns true.

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

This returns false, because 128 is not in the static cache. So a new Integer is created for each side of the equality. Since there are two different Integer objects, and == for objects only returns true if both sides are the exact same object, this is going to be false.

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

This is comparing the primitive int value 128 on the left, with a newly created Integer object on the right. But because it doesn't make sense to compare an int to an Integer, Java will auto-unbox the Integer before doing the comparison; so you end up comparing an int to an int. Since the primitive 128 is equal to itself, this returns true.

Rhebarhee answered 2/1, 2014 at 6:7 Comment(1)
Best explanation here.Avenge
F
14

Take care of returning values from these methods. The valueOf method returns an Integer instance:

public static Integer valueOf(int i)

The parseInt method returns integer value (primitive type):

public static int parseInt(String s) throws NumberFormatException

Explanation for comparison:

In order to save memory, two instances of the wrapper objects , will always be == when their primitive values are the same:

  • Boolean
  • Byte
  • Character from \u0000 to \u007f (7f is 127 in decimal)
  • Short and Integer from -128 to 127

When == is used to compare a primitive to a wrapper, the wrapper will be unwrapped and the comparison will be primitive to primitive.

In your situation (according to the above rules):

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

This expression compares references to the same object because it contains Integer value between -128 and 127 so it returns true.

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

This expression compares references to different objects because they contain Integer values not in <-128, 127> so it returns false.

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

This expression compares primitive value (left hand side) and reference to the object (right hand side) so right hand side will be unwrapped and his primitive type will be compared to the left so it returns true.

Fingering answered 2/1, 2014 at 9:11 Comment(2)
Similar question: #9824553Fingering
Can you provide a URL for the quotation source?Squalid
H
6

Integer objects caches between -128 and 127 of 256 Integer

You should not compare object references with == or !=. You should use .equals(..) instead, or better - use the primitive int rather than Integer.

parseInt: Parses the string argument as a signed decimal integer. The characters in the string must all be decimal digits, except that the first character may be an ASCII minus sign '-' ('\u002D') to indicate a negative value. The resulting integer value is returned, exactly as if the argument and the radix 10 were given as arguments to the parseInt(java.lang.String, int) method.

valueOf Returns an Integer object holding the value extracted from the specified String when parsed with the radix given by the second argument. The first argument is interpreted as representing a signed integer in the radix specified by the second argument, exactly as if the arguments were given to the parseInt(java.lang.String, int) method. The result is an Integer object that represents the integer value specified by the string.

equivalent to

new Integer(Integer.parseInt(s, radix))

radix - the radix to be used in interpreting s

so if you equal Integer.valueOf() for the integer inbetween

-128 to 127 it returns true in your condition

for lesser than -128 and greater than 127 it gives false

Handmaid answered 2/1, 2014 at 5:57 Comment(0)
S
6

To complement the given answers, also take note of the following:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

This code will also print: false

As user Jay has claimed in a comment for the accepted answer, care must be taken when using operator == on objects, here you're checking if both references are the same, which is not, because they are different objets, although they represent the very same value. To compare objects, you should use the equals method instead:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

This will print: true

You may ask, But then why the first line printed true?. Checking the source code for the Integer.valueOf method, you can see the following:

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);
}

If the param is an integer between IntegerCache.low (defaulted to -128) and IntegerCache.high (calculated at runtime with minimum value 127) then a pre-allocated (cached) object is returned. So when you use 127 as parameter, you're getting two references to same cached object and getting true in the comparison of the references.

Swaggering answered 2/1, 2014 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.