The Integer cache and ==
The method same compares two Integers. Let's see what it answers for two pairs of equal numbers:
public class Main {
static boolean same(Integer x, Integer y) {
return x == y;
}
public static void main(String[] args) {
System.out.println(same(100, 100)); // true
System.out.println(same(1000, 1000)); // false
}
}One method, equal numbers in each pair, and different answers. Code like this passes its checks on small numbers and breaks on large ones, when people are already using the program. Interviewers love this question, so let's get to the bottom of it.
Back to the coins and envelopes. When same(100, 100) is called, autoboxing puts each number into an envelope, an Integer object. And == on objects compares references, as it does for arrays and strings. It asks "is this the same envelope?", not "do the envelopes hold equal coins?".
So both 100s ended up in one and the same envelope. Where did it come from?
The Integer cache
Small numbers come up in programs all the time: counters, indices, month numbers. Making a new envelope for each one would be wasteful. So the Integer class prepares an object for every number from -128 to 127 in advance and keeps them on one tray. This is the Integer cache.
Autoboxing calls Integer.valueOf, and it looks at the tray first:
- for a number from
-128to127, it hands out the ready object from the cache, the same one on every call; - for any other number, it creates a new object, a separate one each time.
The widget has four lines: Integer a = 127;, Integer b = 127;, Integer c = 128; and Integer d = 128;. The cache tray is visible from the very start: it is ready before the first line of main. Run the lines one at a time and watch where each variable's object comes from. Then try to guess each comparison, and only then press it.
1Integer a = 127;2Integer b = 127;3Integer c = 128;4Integer d = 128;
The Integer cache is ready in advance: it holds one object for every number from −128 to 127. Press the button to run the first line.
Let's see what we got:
a == bgivestrue: both timesvalueOf(127)handed out the same object from the tray.c == dgivesfalse: the number128is not in the cache, and eachvalueOf(128)created its own object.c.equals(d)givestrue:equalsonIntegercompares the numbers, not the references.
The Java specification requires the numbers from -128 to 127 to always come from the cache. By default the cache stops there, though its upper bound can be raised with a JVM option.
Here the tray picture is off. You take an envelope off a real tray, and then it is no longer there. But an object from the cache is one for the whole program, and every variable shares it. That is safe only because the number inside an Integer cannot be changed.
Compare with equals
Let's fix the method same: make it compare the numbers, not the references.
public class Main {
static boolean same(Integer x, Integer y) {
return x.equals(y);
}
public static void main(String[] args) {
System.out.println(same(100, 100)); // true
System.out.println(same(1000, 1000)); // true
}
}Now both pairs give true, and the cache no longer affects the answer. What to remember about == with wrappers:
Integer == Integercompares references: it istrueonly for one object, such as one from the cache;Integer == intunboxes the wrapper and compares the numbers: withInteger c = 128;the expressionc == 128givestrue;equalsalways compares the numbers, in any range.
In an interview the question goes like this: why does == on two Integers holding 127 give true, and on two holding 128 give false? A good answer has three parts:
- autoboxing calls
Integer.valueOf; valueOfhands out ready objects from the cache for numbers from-128to127and creates new ones for the rest;==compares references, so wrappers are compared withequals.
Common mistake
A limit check is written with ==:
Integer limit = 500;
Integer used = 500;
if (used == limit) { // ✗
System.out.println("limit reached");
}The program prints nothing and reports no error. The number 500 is not in the cache, so used and limit lead to two different objects, and == gives false. Write used.equals(limit), or keep such numbers in an int.
Key points
- Autoboxing calls
Integer.valueOf, which for numbers from-128to127hands out an object from the cache, the same one on every call. - For numbers outside the cache, every boxing creates a new object.
==between twoIntegers compares references, so it givestruewhen they hold127andfalsewhen they hold128.- Wrapped numbers are compared with
equals;Integer == intcompares numbers because it unboxes the wrapper.