Have you ever encountered a puzzling situation in Java where 128 == 128 evaluates to false, yet 127 == 127 surprisingly returns true? This seemingly contradictory behavior often stumps even experienced developers and is a common source of confusion when working with Java’s primitive wrappers. It highlights a critical distinction between comparing object references and comparing their actual values. Understanding this nuance is fundamental to writing robust and error-free Java code, especially when dealing with the Integer wrapper class and its unique optimizations. We’ll delve into the underlying mechanisms that cause this specific anomaly, exploring how Java handles integer caching and autoboxing, and more importantly, how to avoid common pitfalls in your daily coding practices.
The Fundamental Difference: == vs. .equals() for Objects
In Java, the == operator behaves differently depending on whether you’re comparing primitive types (like int, boolean, char) or reference types (objects). When used with primitive types, == compares their actual values. For instance, 5 == 5 is always true because it compares the literal numerical values. However, when == is used with objects, it compares their memory addresses or references. This means == checks if two object references point to the exact same object in memory, not if their contents are identical.
Consider two Integer objects created explicitly using the new keyword:
Integer a = new Integer(128); Integer b = new Integer(128); System.out.println(a == b); // This will always print false
Even though both a and b hold the value 128, new Integer(128) creates two distinct objects in memory. Therefore, their references are different, and a == b correctly evaluates to false. To compare the actual values of objects, Java provides the .equals() method. This method, when properly overridden in a class (as it is in Integer), compares the content or state of the objects. So, a.equals(b) would return true because both objects represent the same integer value.
This distinction between object identity and value equality is crucial and forms the basis for understanding the 127/128 puzzle. Always remember that for objects, == checks identity, while .equals() checks value, unless explicitly stated otherwise by the class’s contract.
Autoboxing and the IntegerCache Mechanism
The anomaly of 127 == 127 being true while 128 == 128 is false stems from Java’s autoboxing feature and an internal optimization known as the IntegerCache. Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes (e.g., converting an int to an Integer). When you write Integer x = 127;, the compiler internally translates this to Integer x = Integer.valueOf(127);.
This is where the IntegerCache comes into play. To optimize performance and reduce memory consumption for frequently used small integer values, Java maintains an internal cache of Integer objects. This cache stores Integer instances for values ranging from -128 to 127 (inclusive). When Integer.valueOf(int i) is called with an int value within this range, it returns a reference to an already existing Integer object from the cache instead of creating a new one. This ensures that for any integer x where -128 <= x <= 127, Integer.valueOf(x) will always return the same object reference.
When you use Integer x = 127; and Integer y = 127;, both statements implicitly call Integer.valueOf(127). Since 127 falls within the cached range, both calls return a reference to the same Integer object from the cache. Therefore, x == y evaluates to true because they are indeed the same object. Conversely, when you use Integer a = 128; and Integer b = 128;, the value 128 falls outside the default cache range. Consequently, Integer.valueOf(128) creates a new Integer object each time it’s called. As a result, a and b refer to two different objects in memory, making a == b evaluate to false.
Let’s break down the two scenarios to solidify our understanding of why is 128==128 false but 127==127 is Question & Answer :
class D { public static void main(String args[]) { Integer b2=128; Integer b3=128; System.out.println(b2==b3); } }
Output:
false
class D { public static void main(String args[]) { Integer b2=127; Integer b3=127; System.out.println(b2==b3); } }
Output:
true
Note: Numbers between -128 and 127 are true.
When you compile a number literal in Java and assign it to a Integer (capital I) the compiler emits:
Integer b2 =Integer.valueOf(127)
This line of code is also generated when you use autoboxing.
valueOf is implemented such that certain numbers are “pooled”, and it returns the same instance for values smaller than 128.
From the java 1.6 source code, line 621:
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
The value of high can be configured to another value, with the system property.
-Djava.lang.Integer.IntegerCache.high=999
If you run your program with that system property, it will output true!
The obvious conclusion: never rely on two references being identical, always compare them with .equals() method.
So b2.equals(b3) will print true for all logically equal values of b2,b3.
Note that Integer cache is not there for performance reasons, but rather to conform to the JLS, section 5.1.7; object identity must be given for values -128 to 127 inclusive.
Integer#valueOf(int) also documents this behavior:
this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.