How do you build a flow from a numeric range or an array, and what are the gotchas with large ranges?
answer
- (1..n).asFlow() for ranges
- intArrayOf(...).asFlow() and Array<T>.asFlow()
- flowOf is varargs — avoid spreading big collections
- asFlow streams without a vararg copy
- take() short-circuits a huge range flow
basics
~10 sUse (1..n).asFlow() for a range or myArray.asFlow() for an array. Each number or element is emitted in order. With huge ranges, prefer asFlow over flowOf so you don't pass thousands of varargs.
solid answer
~40 sRanges convert via IntRange.asFlow()/LongRange.asFlow(), arrays via Array<T>.asFlow() and primitive variants like IntArray.asFlow(). Each emits its elements in order as a cold flow. The key gotcha: flowOf takes varargs, so flowOf(*bigArray) or spreading a large range materializes everything and can hit method/argument limits and waste memory — asFlow streams from the source without a vararg copy. Ranges are lazy element-by-element under asFlow, so a 1..1_000_000 flow doesn't allocate a million-element array. Combine with operators like take() to short-circuit: (1..Int.MAX_VALUE).asFlow().take(5) only emits five values because cold flows stop producing once the downstream cancels. Also remember the source is re-iterated on every collect since the flow is cold.
code
kotlin · 9 linesimport kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
(1..Int.MAX_VALUE).asFlow()
.map { it * it }
.take(4)
.collect { println(it) } // 1 4 9 16
}go deeper
Can write (1..n).asFlow() and array.asFlow() to emit elements.
Explains the varargs vs streaming distinction and lazy range emission.
Uses take() to bound large/unbounded range flows and reasons about cold cancellation propagation.
Considers memory/allocation profiles and cancellation semantics when choosing builders at scale.
## Building from ranges Kotlin ranges (`IntRange`, `LongRange`) have dedicated `asFlow()` overloads: ```kotlin (1..5).asFlow().collect { print(it) } // 12345 (0L..3L).asFlow().collect { print(it) } // 0123 ``` Elements are emitted lazily one at a time — a large range does **not** allocate a backing array of all values. ## Building from arrays There are `asFlow()` extensions for object arrays and each primitive array type: ```kotlin arrayOf("a", "b").asFlow().collect { print(it) } // ab intArrayOf(1, 2, 3).asFlow().collect { print(it) } // 123 ``` ## The varargs gotcha `flowOf(vararg elements: T)` takes **varargs**. Spreading a large array or range into it (`flowOf(*bigArray)`) copies all elements into a vararg array and can be memory-heavy or hit argument limits. `asFlow()` reads directly from the source, avoiding that copy: ```kotlin val big = IntArray(1_000_000) { it } big.asFlow() // good: streams, no vararg copy // flowOf(*big.toTypedArray()) // bad: materializes everything ``` ## Short-circuiting with take Because flows are **cold** and stop producing once downstream cancels, you can build an effectively unbounded range flow and bound it with `take`: ```kotlin (1..Int.MAX_VALUE).asFlow() .take(5) .collect { print(it) } // 12345 — stops after 5 ``` `take` throws an internal cancellation after the limit, so upstream stops emitting. ## Cold re-iteration Every `collect` re-iterates the range/array from the start, since the flow is cold. The source range/array is reusable, so this is safe — unlike a one-shot iterator.
- Why prefer (1..1_000_000).asFlow() over flowOf with a spread array?asFlow streams elements lazily from the range without materializing a vararg array, saving memory and avoiding argument-count limits.
- How can take(5) stop an effectively infinite range flow?Cold flows stop producing once downstream cancels; take cancels collection after the limit, so upstream emission halts.
saying these in an interview costs you the question
- Spreading a million-element array into flowOf
- Claiming (1..1_000_000).asFlow() allocates a million-element list up front
- Thinking you can't bound an infinite range flow
- Forgetting the range is re-iterated on each collect