The trouble with auto boxing and small numbers.
Or why (Integer) 0 == (Integer) 0 but (Integer) 128 != (Integer) 128
Try ...
System.out.println((Integer) 0 == (Integer) 0);
System.out.println((Integer) 128 != (Integer) 128);
Both return true! Now try
double neg_zero = -0.0;
System.out.println(neg_zero == neg_zero);
System.out.println((Double) neg_zero != (Double) neg_zero);
Both are true!
In Joshua Bloch and
Neal Gafter's excellent book
Java Puzzler there appears an example comparator.
public int compare(Integer i1, Integer i2) {
return (i2 < i1 ? -1 : (i2 == i1 ? 0 : 1));
}
This is to compare number is descending order. The author's have already warned against using
== for arithmetic comparison of Number classes however has fallen into the trap here. I asked myself wouldn't checking even a trivial value show a problem with this. Then it occurred to me that it wouldn't.
Consider the following puzzler. What will this printout? Hint: It is neither 0, 231 nor 232
public class EqualsCount {
public static void main(String... args) {
Comparator<Integer> comparator = new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
return (i2 < i1 ? -1 : (i2 == i1 ? 0 : 1));
}
};
long count = 0;
int i = Integer.MIN_VALUE;
do {
if (comparator.compare(i,i) == 0) count++;
} while(i++ < Integer.MAX_VALUE);
System.out.println("Equals count= " + count);
}
}
The program runs for some time but the output is
Equals count= 256
So what is happening? At first you might have assumed that the answer was 232 However == compares object references, not the value of the numbers in those object nor does it call equals()
So you might assume it is 0. You might assume that auto-boxing the int i uses new
Integer(int) however it doesn't directly, instead it uses Integer.valueOf(int i)
private static class IntegerCache {
private IntegerCache(){}
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}
/**
* Returns a Integer instance representing the specified
* int value.
* If a new Integer instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Integer(int)}, as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param i an int value.
* @return a Integer instance representing i.
* @since 1.5
*/
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
A fixed cache is used for auto-boxing byte, char, short, int and long, but not float and double.
Here is another puzzler and the answer is not 0, 256, or 65536
public class EqualsCount {
public static void main(String... args) {
long count = 0;
char ch = Character.MIN_VALUE;
do {
if ((Character) ch == (Character) ch) count++;
} while(ch++ < Character.MAX_VALUE);
System.out.println("Equals count= " + count);
}
}