171

As of Java 1.5, you can pretty much interchange Integer with int in many situations.

However, I found a potential defect in my code that surprised me a bit.

The following code:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

appeared to be incorrectly setting mismatch when the values were equal, although I can't determine under what circumstances. I set a breakpoint in Eclipse and saw that the Integer values were both 137, and I inspected the boolean expression and it said it was false, but when I stepped over it, it was setting mismatch to true.

Changing the conditional to:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

fixed the problem.

Can anyone shed some light on why this happened? So far, I have only seen the behavior on my localhost on my own PC. In this particular case, the code successfully made it past about 20 comparisons, but failed on 2. The problem was consistently reproducible.

If it is a prevalent problem, it should be causing errors on our other environments (dev and test), but so far, no one has reported the problem after hundreds of tests executing this code snippet.

Is it still not legitimate to use == to compare two Integer values?

In addition to all the fine answers below, the following stackoverflow link has quite a bit of additional information. It actually would have answered my original question, but because I didn't mention autoboxing in my question, it didn't show up in the selected suggestions:

Why can't the compiler/JVM just make autoboxing “just work”?

Community
  • 1
  • 1
Jeremy Goodell
  • 16,559
  • 4
  • 33
  • 51

7 Answers7

271

The JVM is caching Integer values. Hence the comparison with == only works for numbers between -128 and 127.

Refer: #Immutable_Objects_.2F_Wrapper_Class_Caching

Naman
  • 23,555
  • 22
  • 173
  • 290
Adam
  • 39,529
  • 15
  • 101
  • 139
  • 1
    Thanks, that certainly explains why 137 fails! And it also answers my question about why it's not a prevalent problem, in 95% of the cases I'm going to encounter, the value would be under 127. Good to catch this now though for the 5% where it isn't. – Jeremy Goodell Sep 03 '10 at 17:36
  • 1
    Interesting side note: up until a couple weeks ago, cdiCt and cdsCt were both ints so this was fine, but I had to make them Integers in order to check for the null situation which is handled differently ... – Jeremy Goodell Sep 03 '10 at 17:40
  • 3
    @Jeremy Yeah, it's a pretty obscure problem, but as a general rule you use .equals() for Objects and == for primitives. You can't rely on autounboxing for equality testing. – Adam Sep 03 '10 at 17:50
  • 1
    Lol, check mark back to you then! Looks like Colin has more than enough points already anyway. – Jeremy Goodell Sep 03 '10 at 18:28
  • on my machine `System.out.println(128L == 128);` prints out `true`. Could you give an example that doesn't work? – kiedysktos May 26 '17 at 13:08
  • Try `System.out.println(new Integer(128) == new Integer(128));` – Adam May 30 '17 at 23:38
  • 2
    Note that new Integer(1) != new Integer(1) as well. new ALWAYS returns a new address. Autoboxing uses a cached version. Other ways that return Integers (without newing them) probably return the cached value as well. – Bill K Jun 23 '17 at 22:41
82

You can't compare two Integer with a simple == they're objects so most of the time references won't be the same.

There is a trick, with Integer between -128 and 127, references will be the same as autoboxing uses Integer.valueOf() which caches small integers.

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.


Resources :

On the same topic :

Community
  • 1
  • 1
Colin Hebert
  • 85,401
  • 13
  • 150
  • 145
  • 1
    Is the guarantee from the JLS or just for the Oracle JVM? – Thorbjørn Ravn Andersen Aug 12 '12 at 10:25
  • The quoted part is from the JLS, so it's a guarantee from the JLS – Colin Hebert Aug 13 '12 at 12:19
  • 1
    Re: guarantee. I still wouldn't rely on it too much. `new Integer(1) == new Integer(1)` is still false. – Thilo Apr 21 '15 at 00:35
  • 1
    @Thilo `new ... == new ...` is always `false`. – MC Emperor Jan 24 '17 at 07:57
  • @MCEmperor: exactly. So just get into the habit of never using `==`. If you get the two integers as parameters from somewhere, you have no way to know if they were created from that cache or not. – Thilo Jan 24 '17 at 09:24
  • 3
    @Thilo True, always use `equals()` when dealing with objects. This should be one of the first things one should know when learning Java. By the way, I would have guessed that the constructor of `Integer` was private, i.e. that instances were always created through the `valueOf()` method. But I see that the constructor is public. – MC Emperor Jan 24 '17 at 09:33
  • @MCEmperor: The constructor not being private must have been an oversight back in the day. `valueOf` and the caching was added later. – Thilo Jan 24 '17 at 10:24
  • One reason to keep the constructor `public` besides backwards compatibility is that it's slightly faster to create an `Integer` object without checking the cache, if you know it's probably not going to be in the cache. At some point the cost of doing a cache check for every value outweighs the extra cost of garbage-collecting the few extra objects that would have been in the cache. – kaya3 May 04 '21 at 04:35
5

The issue is that your two Integer objects are just that, objects. They do not match because you are comparing your two object references, not the values within. Obviously .equals is overridden to provide a value comparison as opposed to an object reference comparison.

buræquete
  • 12,943
  • 4
  • 34
  • 69
MattC
  • 12,163
  • 10
  • 51
  • 77
5

Integer refers to the reference, that is, when comparing references you're comparing if they point to the same object, not value. Hence, the issue you're seeing. The reason it works so well with plain int types is that it unboxes the value contained by the Integer.

May I add that if you're doing what you're doing, why have the if statement to begin with?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
wheaties
  • 34,173
  • 12
  • 82
  • 126
4

"==" always compare the memory location or object references of the values. equals method always compare the values. But equals also indirectly uses the "==" operator to compare the values.

Integer uses Integer cache to store the values from -128 to +127. If == operator is used to check for any values between -128 to 127 then it returns true. for other than these values it returns false .

Refer the link for some additional info

vijay
  • 921
  • 6
  • 15
2

Besides these given great answers, What I have learned is that:

NEVER compare objects with == unless you intend to be comparing them by their references.

ZhaoGang
  • 3,447
  • 18
  • 28
  • 1
    That's because `==` more or less compares values on the stack. So for primitives it's their values and for objects it's their references (hashcode). `.equals` compares whatever was defined in the overriden method of the corresponding class. Note that if there's no override, the default `.equals` in `Object` does the following: `return (this == obj);`. – Terran Sep 21 '20 at 09:03
2

As well for correctness of using == you can just unbox one of compared Integer values before doing == comparison, like:

if ( firstInteger.intValue() == secondInteger ) {..

The second will be auto unboxed (of course you have to check for nulls first).

Mc Bton
  • 101
  • 1
  • 4