tl;dr my opinion is to use a unary +
to trigger the unboxing on one of the operands when checking for value equality, and simply use the maths operators otherwise. Rationale follows:
It has been mentioned already that ==
comparison for Integer
is identity comparison, which is usually not what a programmer want, and that the aim is to do value comparison; still, I've done a little science about how to do that comparison most efficiently, both in term of code compactness, correctness and speed.
I used the usual bunch of methods:
public boolean method1() {
Integer i1 = 7, i2 = 5;
return i1.equals( i2 );
}
public boolean method2() {
Integer i1 = 7, i2 = 5;
return i1.intValue() == i2.intValue();
}
public boolean method3() {
Integer i1 = 7, i2 = 5;
return i1.intValue() == i2;
}
public boolean method4() {
Integer i1 = 7, i2 = 5;
return i1 == +i2;
}
public boolean method5() { // obviously not what we want..
Integer i1 = 7, i2 = 5;
return i1 == i2;
}
and got this code after compilation and decompilation:
public boolean method1() {
Integer var1 = Integer.valueOf( 7 );
Integer var2 = Integer.valueOf( 5 );
return var1.equals( var2 );
}
public boolean method2() {
Integer var1 = Integer.valueOf( 7 );
Integer var2 = Integer.valueOf( 5 );
if ( var2.intValue() == var1.intValue() ) {
return true;
} else {
return false;
}
}
public boolean method3() {
Integer var1 = Integer.valueOf( 7 );
Integer var2 = Integer.valueOf( 5 );
if ( var2.intValue() == var1.intValue() ) {
return true;
} else {
return false;
}
}
public boolean method4() {
Integer var1 = Integer.valueOf( 7 );
Integer var2 = Integer.valueOf( 5 );
if ( var2.intValue() == var1.intValue() ) {
return true;
} else {
return false;
}
}
public boolean method5() {
Integer var1 = Integer.valueOf( 7 );
Integer var2 = Integer.valueOf( 5 );
if ( var2 == var1 ) {
return true;
} else {
return false;
}
}
As you can easily see, method 1 calls Integer.equals()
(obviously), methods 2-4 result in exactly the same code, unwrapping the values by means of .intValue()
and then comparing them directly, and method 5 just triggers an identity comparison, being the incorrect way to compare values.
Since (as already mentioned by e.g. JS) equals()
incurs an overhead (it has to do instanceof
and an unchecked cast), methods 2-4 will work with exactly the same speed, noticingly better than method 1 when used in tight loops, since HotSpot is not likely to optimize out the casts & instanceof
.
It's quite similar with other comparison operators (e.g. <
/>
) - they will trigger unboxing, while using compareTo()
won't - but this time, the operation is highly optimizable by HS since intValue()
is just a getter method (prime candidate to being optimized out).
In my opinion, the seldom used version 4 is the most concise way - every seasoned C/Java developer knows that unary plus is in most cases equal to cast to int
/.intValue()
- while it may be a little WTF moment for some (mostly those who didn't use unary plus in their lifetime), it arguably shows the intent most clearly and most tersely - it shows that we want an int
value of one of the operands, forcing the other value to unbox as well. It is also unarguably most similar to the regular i1 == i2
comparison used for primitive int
values.
My vote goes for i1 == +i2
& i1 > i2
style for Integer
objects, both for performance & consistency reasons. It also makes the code portable to primitives without changing anything other than the type declaration. Using named methods seems like introducing semantic noise to me, similar to the much-criticized bigInt.add(10).multiply(-3)
style.
==
instead ofequals
yields the correct result, that may be because the boxed numbers are being interned or otherwise reused (as a compiler optimization, presumably). The reason to ask this question is to find out what's happening internally, not what appears to be happening. (At least, that's why I'm here.) – Mount