Skip to content
Theory · Wrappers and equality · lesson 5 of 8

The Integer cache and ==

The method same compares two Integers. Let's see what it answers for two pairs of equal numbers:

JAVA
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 -128 to 127, 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.

Live127 from the cache, 128 a new objectvariables: 0 · objects: 0
object numbers are illustrative

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.

Watch where each variable's object comes from

Let's see what we got:

  1. a == b gives true: both times valueOf(127) handed out the same object from the tray.
  2. c == d gives false: the number 128 is not in the cache, and each valueOf(128) created its own object.
  3. c.equals(d) gives true: equals on Integer compares 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.

JAVA
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 == Integer compares references: it is true only for one object, such as one from the cache;
  • Integer == int unboxes the wrapper and compares the numbers: with Integer c = 128; the expression c == 128 gives true;
  • equals always 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;
  • valueOf hands out ready objects from the cache for numbers from -128 to 127 and creates new ones for the rest;
  • == compares references, so wrappers are compared with equals.

Common mistake

A limit check is written with ==:

JAVA
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 -128 to 127 hands out an object from the cache, the same one on every call.
  • For numbers outside the cache, every boxing creates a new object.
  • == between two Integers compares references, so it gives true when they hold 127 and false when they hold 128.
  • Wrapped numbers are compared with equals; Integer == int compares numbers because it unboxes the wrapper.