skip to content

Explain the difference between chunked(n) and windowed(size, step). When do they produce identical results?

level: middleimportance: must knowfreq 60%

answer

  1. chunked = non-overlapping blocks, keeps remainder
  2. windowed = sliding, step controls overlap
  3. partialWindows default = false -> drops short tail
  4. Equal when step==size AND partialWindows==true
  5. Both have transform overload + lazy on Sequence

basics

~20 s

chunked(n) cuts a list into non-overlapping blocks of n. windowed(size, step) slides a window of size, moving by step each time, so windows can overlap. chunked(n) equals windowed(n, n) but also keeps a smaller final chunk.

solid answer

~40 s

chunked(n) splits an Iterable into consecutive, non-overlapping sublists of at most n elements; the last chunk may be smaller. windowed(size, step = 1, partialWindows = false) produces a sliding sequence of sublists of length size, advancing the start index by step each time. With step == size and no overlap, windows are adjacent like chunks. They coincide when step == size AND partialWindows == true (so a short trailing window is kept) — chunked(n) is essentially windowed(n, n, partialWindows = true). By default windowed drops trailing partial windows (partialWindows = false), whereas chunked always keeps the remainder. Both have transform overloads: chunked(n) { it.sum() } and windowed(size, step) { it.average() } map each sublist without exposing the intermediate List, and both work lazily on Sequence.

code

kotlin · 5 lines
kotlin
val xs = listOf(1, 2, 3, 4, 5)
println(xs.chunked(2))                                   // [[1,2],[3,4],[5]]
println(xs.windowed(2, step = 2))                        // [[1,2],[3,4]]  (tail dropped)
println(xs.windowed(2, step = 2, partialWindows = true)) // [[1,2],[3,4],[5]]
println(xs.windowed(3))                                  // [[1,2,3],[2,3,4],[3,4,5]]

go deeper

for a junior

Knows chunked makes non-overlapping groups; may be fuzzy on windowed parameters.

for a middle

Explains size/step/partialWindows precisely and states the equality condition.

for a senior

Discusses tail-dropping default pitfalls, transform overloads to cut allocations, and lazy Sequence behavior.

for a principal

Reasons about streaming/large-data use, snapshot semantics of the passed window, and choosing windowed vs a manual ring buffer for performance.

## chunked(n) `chunked(n)` divides a collection into **consecutive, non-overlapping** sublists, each of size `n`, except the **last one which may be shorter** (it holds the remainder). It never drops elements. ```kotlin listOf(1, 2, 3, 4, 5).chunked(2) // [[1, 2], [3, 4], [5]] <- last chunk has 1 element ``` ## windowed(size, step, partialWindows) `windowed(size, step = 1, partialWindows = false)` returns a **sliding window**: a sublist of length `size`, then advance the start by `step`, repeat. - `size` — window length. - `step` — how far the start moves each iteration (default `1`, which gives heavy overlap). - `partialWindows` — if `true`, keep trailing windows shorter than `size`; if `false` (default), **drop** them. ```kotlin listOf(1, 2, 3, 4, 5).windowed(3) // step 1, partial false // [[1,2,3], [2,3,4], [3,4,5]] listOf(1, 2, 3, 4, 5).windowed(2, step = 2) // adjacent, non-overlapping // [[1,2], [3,4]] <- the trailing [5] is dropped (partialWindows = false) ``` ## When they are identical They coincide when **`step == size` AND `partialWindows == true`**: ```kotlin val xs = listOf(1, 2, 3, 4, 5) xs.chunked(2) == xs.windowed(2, step = 2, partialWindows = true) // true // both -> [[1,2], [3,4], [5]] ``` With the **default** `partialWindows = false`, `windowed(n, step = n)` differs from `chunked(n)` precisely when `size` does not divide the length, because windowed throws away the short final block while chunked keeps it. ## Transform overloads (avoid intermediate lists) Both accept a `transform` lambda applied to each sublist, so you never materialize the inner `List`: ```kotlin listOf(1, 2, 3, 4).chunked(2) { it.sum() } // [3, 7] listOf(1.0, 2.0, 3.0, 4.0).windowed(2) { it.average() } // [1.5, 2.5, 3.5] ``` ## Laziness On an `Iterable` the result is a `List`. On a `Sequence`, both are **lazy** and yield a `Sequence<List<T>>`, suitable for large or streaming inputs. The transform lambda receives a **snapshot view** of each window, so don't store the passed list reference for later use across iterations.

  • What is windowed(2, step = 2) on [1,2,3,4,5] and why?
    [[1,2],[3,4]]. step equals size so windows are adjacent, but partialWindows defaults to false, so the trailing single-element [5] is discarded.
  • How would you compute a 3-element moving average?
    list.windowed(3) { it.average() } — overlapping windows of size 3, step 1, each mapped to its mean.

chunked is slicing a loaf into separate pieces; windowed is dragging a magnifying glass across the text, seeing overlapping spans.

saying these in an interview costs you the question

  • Saying chunked windows overlap
  • Forgetting partialWindows defaults to false, so windowed silently drops the tail
  • Claiming windowed's default step is the window size (it is 1)
  • Thinking chunked drops the remainder like windowed does
  • Not knowing both have transform overloads / lazy Sequence variants

context