Backend Development 6 min read

Understanding Java Integer Caching and Autoboxing: Why == Fails and How to Use equals

This article explains Java's Integer caching mechanism, why using == on Integer objects can give unexpected results outside the -128 to 127 range, how the Integer.valueOf method and the IntegerCache work, and how to inspect the behavior with javap and JVM options.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding Java Integer Caching and Autoboxing: Why == Fails and How to Use equals

According to Alibaba's development manual (OOP rule 7), all comparisons between integer wrapper objects should use equals rather than == because values outside the cached range are distinct objects.

The following code demonstrates the issue:

public class IntegerTest {
    public static void main(String[] args) {
        Integer a = 100,
                b = 100,
                c = 200,
                d = 200;
        System.out.println(a == b);
        System.out.println(c == d);
    }
}

Output:

true
false

Java caches Integer objects in the range -128 to 127 using IntegerCache.cache . Values outside this range are allocated on the heap, so == returns false. The cache size can be increased with the JVM option -XX:AutoBoxCacheMax=<size> , which changes IntegerCache.high .

The source of Integer.valueOf shows the caching logic:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

To see how the compiler translates the code, you can use IDEA to add an external tool that runs javap -c on the compiled class. The command looks like:

/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/javap -c IntegerTest.class

The resulting bytecode confirms that each assignment to an Integer is compiled to a call to Integer.valueOf (lines 2, 8, 15, 22):

Compiled from "IntegerTest.java"
public class com.github.codedrinker.basic.IntegerTest {
  public com.github.codedrinker.basic.IntegerTest();
    Code:
       0: aload_0
       1: invokespecial #1 // Method java/lang/Object.
:()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush 100
       2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: bipush 100
       8: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      11: astore_2
      12: sipush 200
      15: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      18: astore_3
      19: sipush 200
      22: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: astore 4
      ...
}

Other wrapper types such as Character , Long , and Short have similar caching behavior, which can be verified by decompiling their classes in the same way.

JavaperformanceautoboxingequalsJavapIntegerCache
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.