Essence Java Framework
This web site is being migrated to the Essence Wiki
Google
 
Web www.jtoolkit.org weblog.jtoolkit.org
Navigation
Home
SourceForge Home
Articles on Java
Getting Started
Essence Documentation
Download

Contact
Get rewarded for helping out
Email A Question
Weblog

Support This Project

SourceForge.net Logo

Articles Home

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

Copyright 2006 Peter Lawrey Essence Email