Explain the performance/allocation difference between iterating `MyEnum.values()` and `MyEnum.entries`, and when it actually matters.
answer
- values() = defensive copy per call → GC pressure
- entries = cached single instance → zero alloc
- Matters in hot loops / per-request, not cold paths
- Mutable array forces copy; immutable list allows caching
- enumValues<T>() also allocates; enumEntries<T>() caches
basics
~10 svalues() builds a new array every time you call it, which creates garbage. entries reuses one shared list, so it allocates nothing. It matters in hot loops or tight memory situations.
solid answer
~40 s`values()` returns `Array<E>`. Arrays are mutable, so the compiler-generated `values()` must clone the backing array on every invocation to stop callers from mutating shared state — that's one allocation per call plus GC pressure. `entries` is an immutable `EnumEntries<E>` (a `List<E>`); because it can't be mutated, the runtime hands back a single cached instance, so repeated access allocates nothing. The difference is invisible for a one-off call but real in hot paths: a `when` over enum values inside a tight loop, validation in request handlers, or serialization code that touches `values()` per element. Micro-benchmarks aside, the safe default is `entries`. Note `enumValues<E>()` (the inline reified helper) still delegates to `values()`-style array semantics, so it allocates too.
code
kotlin · 7 linesenum class Level { LOW, MEDIUM, HIGH }
// Allocation-free: entries cached once
val levelsByName = Level.entries.associateBy { it.name }
fun parse(name: String): Level =
levelsByName[name] ?: error("unknown $name")go deeper
Recognizes values() makes a copy and entries does not, even without deep GC reasoning.
Explains defensive-copy-per-call vs cached instance and identifies hot loops as where it matters.
Ties immutability to safe caching, mentions enumValues/enumEntries, and avoids over-optimizing cold paths.
Reasons about GC/throughput tradeoffs, multiplatform backends, and sets a team default with guidance on the rare array-needed exceptions.
## The core mechanism The compiler synthesizes `values()` to return `Array<E>`. An **array** is a mutable, fixed-size container; element slots can be reassigned. If `values()` returned a shared array, any caller could write `arr[0] = SomethingElse` and corrupt every future caller. To stay safe, `values()` returns a **fresh defensive copy each call** → **one allocation per call**. `entries` returns `EnumEntries<E>`, which **implements `List<E>`** and is **immutable** (no `add`/`set`). Immutability removes the corruption risk, so the runtime returns a **single cached instance** for the lifetime of the program → **zero allocations** on repeated access. ## Why "allocation" is the keyword Each `values()` call produces a short-lived object (the array). In a hot loop this means: - More work for the **garbage collector** (the JVM subsystem that reclaims unused memory). - Cache/throughput noise from churning short-lived objects. ```kotlin enum class Status { OK, WARN, ERROR } // BAD in a hot path: allocates a new Array on every iteration fun countBad(rows: List<Row>): Int = rows.count { r -> Status.values().any { it == r.status } } // GOOD: entries is cached, no per-iteration allocation fun countGood(rows: List<Row>): Int = rows.count { r -> Status.entries.any { it == r.status } } ``` ## When it actually matters - **Hot loops / per-request work**: validators, parsers, serializers that touch the enum set repeatedly. - **Memory-constrained** targets (Android, embedded). - It is **negligible** for startup-time or one-shot calls — don't over-optimize cold paths. ## Gotchas - Even outside loops, prefer `entries` because it's idiomatic and List-typed (no array-to-list conversion). - `enumValues<T>()` (inline `reified`) is the generic analogue of `values()` and **also allocates** an array; there is no generic `entries` equivalent prior to it being added — use `enumEntries<T>()` (Kotlin 1.9+) for the cached, allocation-free generic form. ## Takeaway Default to `entries`. Use `values()` only when an `Array` is genuinely required (e.g. interop). The win is correctness-by-immutability first, allocation savings second.
- Does `enumValues<T>()` allocate like `values()`?Yes — it has array semantics and allocates per call. Use `enumEntries<T>()` (Kotlin 1.9+) for the cached, allocation-free generic version.
- Is the allocation difference ever worth ignoring?Yes, on cold/one-shot paths it's negligible; readability and correctness drive the choice there, and both favor `entries` anyway.
saying these in an interview costs you the question
- Claiming `values()` is cached / allocation-free.
- Saying `entries` allocates a copy on each access.
- Over-claiming huge perf wins on cold paths.
- Not knowing why immutability enables caching.
- Confusing GC pressure with a memory leak (it's transient garbage, not a leak).