When should you reach for repeat(n) versus a for-loop or value builders like List(n) { } / (0 until n).map { }? Discuss intent, value production, and performance.
answer
- repeat = side effects, fixed count, Unit
- List(n){} = pre-sized value builder
- for-loop = break/continue/custom step
- repeat is inline == manual for-loop perf
- Avoid repeat + mutableListOf.add accumulation
basics
~20 sUse repeat for a fixed number of side-effecting passes when you don't need to collect results. If you need a list of computed values, use List(n) { }. If you need to break early or complex stepping, use a for-loop.
solid answer
~50 srepeat(n) { } is the clearest expression of 'do this side effect n times', giving a zero-based index and returning Unit. It's `inline`, so it compiles to a plain `for` loop with no lambda allocation — perf-equal to a hand-written loop. Choose a `for (i in a until b step s)` loop when you need custom bounds/step, early `break`, or `continue`. Choose value builders when you produce data: `List(n) { i -> ... }` eagerly creates a sized list (pre-allocated, efficient), while `(0 until n).map { }` also builds a list but via an IntRange and is fine for small n. For lazy/large pipelines use `generateSequence`/`asSequence`. The anti-pattern is using `repeat` with an external `mutableListOf()` you `add` to — prefer `List(n) { }` for clarity and pre-sizing. So: side effects -> repeat; values -> List(n){}/map; early-exit or custom stepping -> for.
code
kotlin · 8 lines// Side effect, fixed count:
repeat(retries) { attempt -> sendWithBackoff(attempt) }
// Producing values (prefer over repeat + add):
val grid: List<Cell> = List(width * height) { i -> Cell(i % width, i / width) }
// Early exit / custom step -> for:
for (i in 0 until n step 2) { if (shouldStop(i)) break }go deeper
Knows repeat is for doing something n times and that lists have other builders.
Distinguishes side-effect loops (repeat) from value builders (List(n){}) and picks the right one.
Reasons about inlining/perf parity, pre-sizing, the repeat+add anti-pattern, and when for/sequences win.
Sets idiom guidance across a codebase, weighing readability, allocation, and pipeline laziness for collection construction.
## Decision guide ### Use `repeat(n) { }` when - You need a **fixed count** of iterations. - The body is a **side effect** (printing, mutating, retrying) — not producing a returned collection. - You want clear intent and a zero-based index. ```kotlin repeat(3) { attempt -> tryConnect(attempt) } ``` It's `inline`, so it compiles to essentially: ```kotlin for (index in 0 until 3) { tryConnect(index) } ``` No lambda object is allocated; performance equals a manual loop. ### Use a `for` loop when - You need a **custom range or step**: `for (i in 0 until n step 2)`. - You need **early exit** (`break`) or `continue` (cleaner than `return@repeat` gymnastics). - You iterate a **collection** directly: `for (item in list)`. ### Use value builders when you produce data - `List(n) { i -> compute(i) }` — eagerly builds a list of exactly `n` elements, **pre-sized**, calling the lambda with each index. This is the idiomatic 'make n things' constructor. - `(0 until n).map { i -> compute(i) }` — also produces a list; fine and readable, slightly more indirection (creates an `IntRange`). Good for small/medium `n`. - `MutableList(n) { ... }` when you need mutability. ```kotlin val squares = List(5) { it * it } // [0, 1, 4, 9, 16] ``` ### Use sequences for large/lazy work - `generateSequence` or `(0 until n).asSequence().map { }.filter { }` to avoid building intermediate collections in long pipelines. ## Anti-pattern ```kotlin // Avoid: repeat + manual accumulation val out = mutableListOf<Int>() repeat(n) { out.add(it * it) } // Prefer: pre-sized, declarative val out = List(n) { it * it } ``` The builder form states intent ('a list of n values'), pre-allocates capacity, and avoids resizing. ## Performance summary - `repeat` == `for` (inlined, no allocation). - `List(n) { }` pre-sizes the backing array; cheapest for known size. - `(0 until n).map { }` allocates a range + result list; negligible for small n. - Sequences trade per-element overhead for avoiding intermediate collections; win on large multi-stage pipelines. ## Rule of thumb Side effects, fixed count -> `repeat`. Build a collection -> `List(n) { }`. Early-exit / custom stepping -> `for`. Large lazy pipelines -> sequences.
- Is repeat slower than a hand-written for-loop?No. repeat is inline, so the lambda is inlined and it compiles to the same loop with no extra allocation.
- What's the idiomatic way to create a list of n computed values?Use the List(n) { index -> ... } constructor; it pre-sizes the list and passes each index, beating repeat-plus-add.
- When would you pick a for-loop over repeat?When you need break/continue, a custom step or bounds, or to iterate a collection directly.
repeat is a rubber stamp pressed n times; List(n){} is a mold casting n finished parts.
saying these in an interview costs you the question
- Claiming repeat is slower than a for-loop due to the lambda
- Using repeat + mutableListOf.add when List(n){} fits
- Forcing repeat where early exit is needed instead of a for-loop
- Thinking repeat can return a collection
- Reaching for sequences for tiny fixed counts (needless overhead)