What exactly does comparing Integers with == do?
Asked Answered
C

7

11

EDIT: OK, OK, I misread. I'm not comparing an int to an Integer. Duly noted.

My SCJP book says:

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

So you'd think this code would print true:

    Integer i1 = 1; //if this were int it'd be correct and behave as the book says.
    Integer i2 = new Integer(1);
    System.out.println(i1 == i2);

but it prints false.

Also, according to my book, this should print true:

Integer i1 = 1000; //it does print `true` with i1 = 1000, but not i1 = 1, and one of the answers explained why.
Integer i2 = 1000;
System.out.println(i1 != i2);

Nope. It's false.

What gives?

Chengtu answered 11/9, 2010 at 3:58 Comment(6)
See, I'm not sure if this part of the book is poorly written or I'm an idiot, but I just can't make out what it says the reason is. My best attempt at understanding is that == is treated differently from !=. Maybe == reflects value equality (deep equality) but != doesn't. In any case, what the book says doesn't happen IRL anyway.Chengtu
No, the problem in this case lies in the two types you're comparing. == and != behave in the same way.Sidhu
Just this example is enough to make me think Java is some kind of sick joke instead of a real programming language.Veliger
@Veliger - that's a pretty lame statement. You could have the same problem in C if you were to compare two integer pointers - it's just more explicit.Sidhu
Your second example does print true. See it on ideoneBirkett
it is quite confusing. boxing is very useful and safe. unboxing is not. always be aware of data types in such expressions.Bringhurst
L
8
Integer i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1 == i2);

When you assign 1 to i1 that value is boxed, creating an Integer object. The comparison then compares the two object references. The references are unequal, so the comparison fails.

Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 != i2);

Because these are initialized with compile-time constants the compiler can and does intern them and makes both point to the same Integer object.

(Note that I changed the values from 1000 to 100. As @NullUserException points out, only small integers are interned.)


Here's a really interesting test. See if you can figure this out. Why does the first program print true, but the second one false? Using your knowledge of boxing and compiler time analysis you should be able to figure this out:

// Prints "true".
int i1 = 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

// Prints "false".
int i1 = 0;
Integer i2 = new Integer(i1);
i1 += 1;
System.out.println(i1 == i2);

If you understand the above, try to predict what this program prints:

int i1 = 0;
i1 += 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

(After you guess, run it and see!)

Latten answered 11/9, 2010 at 4:4 Comment(1)
This is important as only small integers are interned. - Keep in mind it's just that Integer just maintains an internal cache that it uses (for said small values) when a call to valueOf is performed; the compiler still generates two lines of invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;Misprision
B
12

Note also that newer versions of Java cache Integers in the -128 to 127 range (256 values), meaning that:

Integer i1, i2;

i1 = 127;
i2 = 127;
System.out.println(i1 == i2);

i1 = 128;
i2 = 128;
System.out.println(i1 == i2);

Will print true and false. (see it on ideone)

Moral: To avoid problems, always use .equals() when comparing two objects.

You can rely on unboxing when you are using == to compare a wrapped primitive to a primitive (eg: Integer with int), but if you are comparing two Integers with == that will fail for the reasons @dan04 explained.

Birkett answered 11/9, 2010 at 4:12 Comment(2)
Interesting! I'd never use == IRL, but this is a very sadistic exam we're talking about :)Chengtu
+1 for the very relevant point. I will nitpick and point out it's -128 to 127, although the 1.6 source that comes with the Sun JDK shows them playing games with the upper bound (it's always at least 127, though).Misprision
L
8
Integer i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1 == i2);

When you assign 1 to i1 that value is boxed, creating an Integer object. The comparison then compares the two object references. The references are unequal, so the comparison fails.

Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 != i2);

Because these are initialized with compile-time constants the compiler can and does intern them and makes both point to the same Integer object.

(Note that I changed the values from 1000 to 100. As @NullUserException points out, only small integers are interned.)


Here's a really interesting test. See if you can figure this out. Why does the first program print true, but the second one false? Using your knowledge of boxing and compiler time analysis you should be able to figure this out:

// Prints "true".
int i1 = 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

// Prints "false".
int i1 = 0;
Integer i2 = new Integer(i1);
i1 += 1;
System.out.println(i1 == i2);

If you understand the above, try to predict what this program prints:

int i1 = 0;
i1 += 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

(After you guess, run it and see!)

Latten answered 11/9, 2010 at 4:4 Comment(1)
This is important as only small integers are interned. - Keep in mind it's just that Integer just maintains an internal cache that it uses (for said small values) when a call to valueOf is performed; the compiler still generates two lines of invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer;Misprision
R
6

You're not comparing a primitive to a wrapper. You're comparing two wrappers (reference types). == compares object identity, which returns false because they're different objects.

Restrictive answered 11/9, 2010 at 4:1 Comment(1)
Not always true, smaller integer values are cached, just as if you would have called Integer.valueOf. So you are in fact comparing the same objects.Ptomaine
H
1

No, I would not think that code print true, and you answered yourself exactly why.

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

and you then went on to compare two Integer references- that is, it compared the memory address of i1 and i2. You wanted either

Integer i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1.equals(i2));

Or

int i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1 == i2);
Hewlett answered 11/9, 2010 at 4:13 Comment(0)
L
1

Note that you misread the excerpt you quoted. The excerpt specifically limits it statement to comparisons like these:

int k = 1;
Integer l = new Integer(1);
System.out.println(l == k);
Loewe answered 11/9, 2010 at 4:15 Comment(0)
S
0

Since Java 5.0, there is automatic boxing and unboxing, meaning that wrappers can be implicitly converted to primitives and vice versa. However, if you compare two Integer objects, you are still comparing two references, and there is nothing that would trigger automatic boxing/unboxing. If that was the case, code written in J2SE 1.4 and prior would break.

Sidhu answered 11/9, 2010 at 4:5 Comment(0)
R
0

Suppose let has have a example

What will be the output of this program code?

public class autoboxing {
public static void main(String a args) {
Integer a = new Integer(127);
Integer b = new Integer(127);
Integer c = 127;
Integer d = 127;
Integer e = new Integer(200);
Integer f = new Integer(200);
Integer g = 200;
Integer h = 200;
System.out.println((a == b) + ' " + (c =-- d) + " " + (e==f)+ " "+ (g == h));

.

When you create an Integer object with a new operator, it returns a new object every time. When you are comparing two reference variables with "==" operator, if two reference variables are referring to two different objects, "==" operator returns false.

So,

(a == b) and (e==f) expressions returns false. Integer class caches values between -128 to 127.

When you are comparing two Integer objects with "==" operator, if those two integer objects are created with autoboxing then value0f(int i) method will be called.

ANSWER: False True False False

Below is the implementation of that method

public static Integer value0f(int i) {
if (i >= IntegerCachedow && i <= IntegerCache.high)
return IntegerCache.cacheli + (-IntegerCachedow));
return new Integer(i);

From the above implementation, below are the conclusions

  1. If two Integer objects values are between -128 to 127, this method returns same values. So (c == d) returns true.

  2. If two Integer objects values are outside of the range -128 to 127, this method returns different new Integer objects. So, (g == h) returns false

More detail about the method here: https://mcmap.net/q/22381/-why-integer-class-caching-values-in-the-range-128-to-127

Rudyrudyard answered 27/8, 2017 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.