MathJax

SyntaxHighlighter

Highlight

Custom CSS

Thursday, February 16, 2012

Java Fun: When 128 != 128

I didn't discover this, but I did think it was fun to share:

public class JavaWTF {
  public static void main(String[] args) {
    System.out.println(isSame(127, 127));  // true
    System.out.println(isSame(128, 128));  // false
  }

  private static boolean isSame(Integer i1, Integer i2) {
    return i1 == i2;
  }
}

This is still true as of JDK 1.6. Why is this so? I took a look at the bytecode to see what's going on, and here's what we have:

Compiled from "JavaWTF.java"
public class JavaWTF extends java.lang.Object{
public JavaWTF();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: bipush  127
   2: istore_1
   3: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   6: new #3; //class java/lang/Integer
   9: dup
   10:  iload_1
   11:  invokespecial #4; //Method java/lang/Integer."<init>":(I)V
   14:  new #3; //class java/lang/Integer
   17:  dup
   18:  iload_1
   19:  invokespecial #4; //Method java/lang/Integer."<init>":(I)V
   22:  if_acmpne 29
   25:  iconst_1
   26:  goto  30
   29:  iconst_0
   30:  invokevirtual #5; //Method java/io/PrintStream.println:(Z)V
   33:  getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   36:  iload_1
   37:  invokestatic  #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   40:  iload_1
   41:  invokestatic  #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   44:  if_acmpne 51
   47:  iconst_1
   48:  goto  52
   51:  iconst_0
   52:  invokevirtual #5; //Method java/io/PrintStream.println:(Z)V
   55:  return
}

(Incidentally, you can get this output by running javap -c JavaWTF.)

So basically, Integer.valueOf is being called to do the Integer boxing. From the 1.6 JavaDocs, I see:

public static Integer valueOf(int i)

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 Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values.

So sometimes it returns cached values, and sometimes it doesn't. If this functions returns an Integer via new Integer all the time, then all == comparisons will fail. However, if it returns the same object for some invocations, we'll get the observed behavior.

At this point, I was stuck - I didn't know where to go to look at some real Java 6 source code, but I bet the Apache Harmony guys knew a thing or two about all the quirks of the Java platform, so I took a peak at their Integer.java class:

public static Integer valueOf(int i) {
    if (i < -128 || i > 127) {
        return new Integer(i);
    }
    return valueOfCache.CACHE [i+128];
}

static class valueOfCache {
    /**
     * <p>
     * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing.
     */
    static final Integer[] CACHE = new Integer[256];
   
    static {
        for(int i=-128; i<=127; i++) {
            CACHE[i+128] = new Integer(i);
        }
    }
}

Ha! So I guess "most frequently used" integers means any integer between -128 and 127 (not integers your program has used before). No wonder!