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
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; }}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 testsImplement 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])
Sign up to check the hidden tests and save your progress. Sign up