$title =

Why 1 == 1 is true but 1000 == 1000 can be false in Java

;

$content = [

If you’ve ever seen this surprising behavior in Java:

Integer a = 1;
Integer b = 1;
System.out.println(a == b);      // true

Integer x = 1000;
Integer y = 1000;
System.out.println(x == y);      // false

— you’re not alone: this trips up many Java developers. Let’s unpack exactly why this happens, what the rules are, what’s guaranteed by the language, how to avoid bugs, and examples you can run.


(short summary)

  • == compares primitive values but object references for reference types.
  • Integer is an object wrapper for the primitive int. When you use autoboxing (e.g., Integer i = 1;), Java may reuse cached Integer objects for certain values.
  • Java caches Integer objects for -128 to 127 by default. So Integer a = 1; Integer b = 1; will point to the same cached object → == returns true.
  • 1000 is outside the cache range, so two autoboxed Integer objects are different objects → == returns false.
  • Always use .equals() (or compare primitive int values) to compare numeric values reliably.

Important concepts to understand first

1. Primitive vs Reference types

  • int is a primitive. int a = 1; int b = 1; → a == b compares values (true).
  • Integer is an object (wrapper class). Integer a = new Integer(1); Integer b = new Integer(1); → a == b compares references (different objects → false) while a.equals(b) compares values (true).

2. Autoboxing / Unboxing

  • Autoboxing: automatic conversion from primitive to wrapper (e.g., int → Integer) when needed.
    • Integer i = 1; is autoboxing int 1 into an Integer.
  • Unboxing: wrapper → primitive when needed (e.g., Integer i; int j = i;).

3. == vs  .equals()

  • == on primitives: compares values.
  • == on object references: compares whether both references point to the same object.
  • .equals() (for wrapper classes) compares values (unless overridden differently).

4. Integer caching (

Integer.valueOf

)

  • Integer.valueOf(int i) may return cached Integer objects for a range of values.
  • The Java SE spec requires caching for at least -128 to 127. Many JVMs allow configuring a higher positive bound (via -Djava.lang.Integer.IntegerCache.high=…) but you should not rely on anything beyond the spec default.
  • Autoboxing uses Integer.valueOf(…) internally.

Concrete example — run this and inspect output

public class IntegerEqualityDemo {
    public static void main(String[] args) {
        // Primitives: always compares values
        int p1 = 1;
        int p2 = 1;
        System.out.println("int 1 == int 1 : " + (p1 == p2)); // true

        // Autoboxed Integers in cache range (-128..127)
        Integer a = 1;   // autoboxing -> Integer.valueOf(1)
        Integer b = 1;   // same cached object
        System.out.println("Integer 1 == Integer 1 : " + (a == b));        // true
        System.out.println("Integer 1 equals Integer 1 : " + a.equals(b)); // true

        // Autoboxed Integers outside cache range
        Integer x = 1000; // autoboxing -> Integer.valueOf(1000) typically creates new object
        Integer y = 1000; // another object
        System.out.println("Integer 1000 == Integer 1000 : " + (x == y));        // usually false
        System.out.println("Integer 1000 equals Integer 1000 : " + x.equals(y)); // true

        // Using new -> always different objects
        Integer n1 = new Integer(1);
        Integer n2 = new Integer(1);
        System.out.println("new Integer(1) == new Integer(1) : " + (n1 == n2));        // false
        System.out.println("new Integer(1) equals new Integer(1) : " + n1.equals(n2)); // true

        // Forcing unboxing: compares primitives
        System.out.println("x.intValue() == y.intValue() : " + (x.intValue() == y.intValue())); // true
        System.out.println("x == 1000 : " + (x == 1000)); // true -> x is unboxed here to primitive int

        // Identity hash codes to show different objects
        System.out.println("System.identityHashCode(a): " + System.identityHashCode(a));
        System.out.println("System.identityHashCode(b): " + System.identityHashCode(b));
        System.out.println("System.identityHashCode(x): " + System.identityHashCode(x));
        System.out.println("System.identityHashCode(y): " + System.identityHashCode(y));
    }
}

Expected output (typical on standard JVM):

int 1 == int 1 : true
Integer 1 == Integer 1 : true
Integer 1 equals Integer 1 : true
Integer 1000 == Integer 1000 : false
Integer 1000 equals Integer 1000 : true
new Integer(1) == new Integer(1) : false
new Integer(1) equals new Integer(1) : true
x.intValue() == y.intValue() : true
x == 1000 : true
System.identityHashCode(a): 460141958
System.identityHashCode(b): 460141958
System.identityHashCode(x): 1163157884
System.identityHashCode(y): 1956725890

System.identityHashCode values show that a and b are the same object (same id), while x and y are different objects (different ids).


Why 

Integer a = 1; Integer b = 1; is  true

  • Integer a = 1; is autoboxing and uses Integer.valueOf(1).
  • For 1 (in -128..127), Integer.valueOf returns a cached shared Integer object.
  • a and b reference the same object, so a == b is true.

Why  Integer x = 1000; Integer y = 1000;  is  false

  • 1000 is outside the mandatory cache range.
  • Integer.valueOf(1000) will typically create separate Integer objects (or at least distinct references).
  • So x and y do not point to the same object; x == y → false.
  • Their .equals() compares numeric values and returns true.

Some more edge cases and useful checks

1. Compiler constant folding for  Integer references

If values are compile-time constants and boxed at compile-time, you may sometimes see surprising behavior. But the safe rule is: do not rely on == for Integer comparison.

2.  short, byte, char, wrappers also have caching behavior

Short, Byte, Character (for small ranges), Long (often cached -128..127) follow similar caching patterns in valueOf.

3. Configuring cache range

Some JVM implementations allow configuring the upper bound of Integer cache with a system property:

-Djava.lang.Integer.IntegerCache.high=1000

But this is implementation-specific and should not be relied upon in application logic.

4. equals() vs  ==

 best practice

Always use .equals() when comparing wrapper numbers for equality of numeric value:

Integer a = 1000;
Integer b = 1000;
if (a.equals(b)) {
    // correct way to check numeric equality
}

Or unbox to primitives to compare:

if (a.intValue() == b.intValue()) { ... }

or

if (a == b.intValue()) { ... } // a unboxed or b unboxed

Performance note

Some people worry about .equals() vs == performance. The overhead of .equals() on wrapper types is negligible for typical application logic and runnable Java code will inline and optimize such calls; correctness is far more important.

Common bugs caused by incorrectly using  ==

  • Comparing values in collections or caches where wrappers are used as keys.
  • Conditional checks: if (myInteger == 0) { … } — this may unbox myInteger and is usually OK, but if myInteger is null you’ll get NullPointerException. Safer: Integer.valueOf(0).equals(myInteger) or Objects.equals(myInteger, 0).
  • HashMap/Set behaviours depending on equals vs identity.

Handy checklist (short)

  • Want to compare numeric value? Use .equals() (or unbox to primitives).
  • Want to check if two references point to the same object? Use ==.
  • Don’t rely on == for wrapper classes — it may produce different results because of caching and object identity.
  • Beware of null when unboxing wrappers — safest is to null-check first.

CodeResult (==)Result (.equals())Explanation
int a=1; int b=1;truen/aprimitive compare
Integer a=1; b=1;truetruecached Integer objects
Integer a=1000; b=1000;falsetrueoutside cache -> distinct objects
Integer a=new Integer(1);falsetruenew always creates new object
Integer a=null; a==0NPE on unboxn/aunboxing null throws NullPointerException

Closing notes

The 1 == 1 vs 1000 == 1000 paradox is not a bug in Java — it’s a side-effect of autoboxing, caching, and the difference between reference comparison and value comparison. Knowing the difference and following the simple rule — use .equals() for wrapper value equality — will prevent most real-world problems.

If you like, I can:

  • Provide a downloadable Java file you can run locally.
  • Show how to detect identity vs value equality in debugging sessions.
  • Create a short unit test demonstrating these cases.

];

$date =

;

$category =

;

$author =

;

$next =

;

Discover more from Terminal-Geeks

Subscribe now to keep reading and get access to the full archive.

Continue reading