Explain the Integer cache (-128..127) and why `==` on boxed Integers is dangerous.
answer
- valueOf caches -128..127; outside that, fresh objects
- == on wrappers = identity, .equals() = value
- Small boxed numbers compare == true; large ones false — silent bug
- Upper bound tunable via -XX:AutoBoxCacheMax; never rely on it
- new Integer defeats the cache and is deprecated
basics
~20 sInteger.valueOf caches small Integer objects from -128 to 127 and reuses them. So two boxed 100s are the same object (== is true), but two boxed 1000s are different objects (== is false). Use .equals() to compare values, not ==, which compares object identity.
solid answer
~50 sWhen you autobox an int, the compiler calls Integer.valueOf(x). For values in -128..127 (the default cache range, configurable via -XX:AutoBoxCacheMax for the upper bound), valueOf returns a pre-built, shared Integer instance instead of allocating. So `Integer a = 100, b = 100; a == b` is true because both reference the same cached object, but `Integer a = 1000, b = 1000; a == b` is false because each boxing allocates a distinct object and == compares references (identity), not value. This makes == on wrappers a correctness trap: it works for small numbers and silently breaks for large ones. The rule is always use .equals() (or unbox to int and compare with ==) for value equality. The cache also has a performance angle: it removes allocation for the most common small values, which is why valueOf is preferred over new Integer.
go deeper
Knows == can behave oddly on Integers and that .equals() is the safe comparison.
States the -128..127 cache, explains identity vs value, and shows why small vs large boxed numbers compare differently with ==.
Connects the cache to the valueOf optimization, knows the tunable upper bound and other wrapper caches, and treats cache reliance as a correctness anti-pattern.
Sets coding standards/static-analysis rules to ban == on boxed types, and reasons about how cache tuning trades a small allocation win against zero correctness benefit.
## The mechanism Autoboxing compiles to `Integer.valueOf(int)`, not to `new Integer(int)`. `valueOf` consults a small static cache. The JDK's `Integer.IntegerCache` eagerly creates one `Integer` object for every value in **-128 to 127** at class-load time. When you box a value in that range, `valueOf` returns the *same cached instance* every time; outside the range it allocates a fresh object. ```java Integer a = 127, b = 127; // both → cached instance Integer c = 128, d = 128; // each → new object ``` ## Why `==` is dangerous on wrappers In Java, `==` on reference types compares **identity** (same object?), while `.equals()` compares **value**. For boxed integers: - `a == b` is `true` — both are the one cached `127`. - `c == d` is `false` — two distinct heap objects holding the value 128. This is the classic trap: code using `==` *appears* correct in tests with small numbers, then fails in production on larger values. It is a silent, data-dependent bug. **The rule:** compare wrapper values with `.equals()`, or unbox to primitives (`int x = c; x == 128`) where `==` then compares raw values correctly. Never use `==` to compare two `Integer` (or `Long`, etc.) references for value equality. ## The cache's purpose and limits The cache exists for **performance**: small integers (loop indices, small counts, booleans-as-0/1) are overwhelmingly the most boxed values, so caching them eliminates the common-case allocation. That is also *why* `valueOf` exists and why `new Integer(...)` is deprecated — `new` defeats the cache and always allocates. The lower bound is fixed at -128. The **upper bound is tunable** via `-XX:AutoBoxCacheMax=<n>` (or the system property `java.lang.Integer.IntegerCache.high`), which can extend caching to larger values. You must never *rely* on this for correctness — code must be correct regardless of cache range; the flag is purely an allocation optimization. Other wrappers have caches too: `Long` (-128..127), `Short`, `Byte`, `Character` (0..127), and `Boolean` (always cached). `Float`/`Double` are **not** cached (continuous values make a cache pointless), so `Double` `==` is essentially always identity-false for distinct boxes. ## Identity vs equality, restated - **Identity** (`==`): are these the exact same object in memory? - **Equality** (`.equals()`): do these objects represent the same value? Autoboxing blurs the two because the cache makes *some* equal values also identical. Treat that as an implementation detail, never a contract.
- Why is `new Integer(5)` deprecated in favor of `Integer.valueOf(5)`?new always allocates a fresh object, defeating the cache and wasting memory, and it gives a misleading impression that identity matters. valueOf returns cached instances for small values and is the form autoboxing already uses, so it's both faster and consistent.
- Does the Integer cache affect `.equals()` results?No. .equals() compares the underlying int value regardless of whether the objects are cached or distinct, so it always gives the correct answer. The cache only affects ==/identity.
saying these in an interview costs you the question
- Using == to compare Integer values in real code
- Claiming the cache range is the whole int range, or that all Integers are interned
- Believing -XX:AutoBoxCacheMax makes == safe — correctness must not depend on cache size
- Assuming Double/Float are cached like Integer (they are not)