Java

Primitives, wrappers, and the == trap

Why int and Integer are not interchangeable, and the caching quirk that makes == look like it works.

After this lesson you can

  • Tell a primitive type from its wrapper class
  • Say what autoboxing does and where it costs something
  • Explain why == on two Integer values sometimes works and sometimes does not

Java has eight primitive types — int, long, double, float, boolean, char, byte, short — and each has a corresponding wrapper class: Integer, Long, Double, and so on. A primitive is a raw value; a wrapper is a real object, which is the only kind of thing a generic collection can hold.

int a = 5;              // a primitive value
Integer b = 5;           // an Integer object — this is autoboxing
List<Integer> nums = new ArrayList<>();
nums.add(5);              // autoboxed to Integer on the way in
int first = nums.get(0);  // auto-unboxed back to int on the way out

List<int> does not compile — generics only work with reference types, which is the entire reason autoboxing exists: it lets 5 be written where an Integer is needed, and the compiler inserts the conversion.

Try it

What a List of int actually holdsjava-21
import java.util.*; public class Solution {    public static String describe() {        List<Integer> nums = new ArrayList<>();        for (int i = 0; i < 3; i++) {            nums.add(i);   // each int autoboxed to a new Integer        }        int sum = 0;        for (int n : nums) {            sum += n;      // each Integer auto-unboxed back to int        }        return "nums=" + nums + " sum=" + sum;    }}
What to look for

The == trap

== on two primitives compares values. == on two objects — which includes wrapper types — compares references: are these the exact same object in memory.

Integer a = 127;
Integer b = 127;
a == b;          // true

Integer c = 200;
Integer d = 200;
c == d;          // false

Both pairs look identical. The difference is that Java caches boxed Integer values from -128 to 127 and reuses the same objects for them — an implementation detail of Integer.valueOf, not a language guarantee. 127 == 127 is comparing two references to the same cached object; 200 == 200 is comparing two freshly allocated objects that happen to hold the same value. The cache range is exactly wide enough to make a bug look like it works in a quick test and fail once real numbers show up.

The fix is the same one every language with this trap needs: compare wrapper objects with .equals(), not ==.

Integer c = 200;
Integer d = 200;
c.equals(d);   // true — always, regardless of caching

Unboxed primitives never have this problem — int c = 200; int d = 200; c == d is true unconditionally, because there is no object identity to compare, only the value.

Try it yourself

2 visible tests · 2 hidden tests

Implement countEqualPairs(values). values is a List<Integer>. Count how many adjacent pairs (values[i], values[i + 1]) are equal in value — use .equals(), not ==, so the answer is correct whether or not Java happened to cache that particular number.

  • countEqualPairs([1,1,2,3,3,3])
  • countEqualPairs([200,200,300])
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up