skip to content

buildList / buildMap Builders

buildList, buildSet, and buildMap hand you a temporary mutable collection inside a block and give back a read-only result. It is the clean way to build a collection conditionally without exposing a mutable variable.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What does the buildList { } function do, and what is the type of the value it returns?

level: juniorimportance: must knowfreq 55%

answer

  1. Mutable receiver inside braces, read-only List out
  2. buildList / buildSet / buildMap
  3. Replaces mutableListOf + toList()
  4. No defensive copy
  5. Optional capacity arg

basics

~10 s

buildList gives you a temporary mutable list to fill inside the braces, then hands back a normal read-only List. You add items with add(), and the final list cannot be changed afterward.

solid answer

~40 s

buildList { } is a standard-library inline function. It creates a fresh MutableList, passes it as the receiver of the lambda (so you call add, addAll, etc. directly), and returns the populated collection typed as the read-only List<T>. The element type is inferred from what you add, or you can specify it: buildList<String> { }. The returned object is the same underlying list but exposed only through the read-only List interface, so callers can't mutate it through that reference. buildSet and buildMap behave identically for Set and Map. It is the idiomatic replacement for the older 'create a mutable list, populate it, then return it (or call toList())' pattern, and avoids a defensive copy because the builder seals the mutable reference once the lambda returns.

code

kotlin · 5 lines
kotlin
val list: List<Int> = buildList {
    add(1)
    addAll(listOf(2, 3))
}
// list is read-only here; list.add(4) would not compile

go deeper

for a junior

Knows buildList gives a mutable list inside the braces and returns a read-only List, and can write a basic example with add/addAll.

for a middle

Explains the receiver-lambda mechanism, the buildSet/buildMap siblings, and why it replaces the mutableListOf + toList idiom without a copy.

for a senior

Discusses the upcast-to-read-only return, the capacity argument, and that the returned reference cannot be mutated even though the backing type is ArrayList.

for a principal

Frames it within the contract of read-only views vs true immutability and how the builder seals the only mutable reference, contrasting with hand-rolled patterns in API design.

## What buildList is `buildList` is a standard-library function (since Kotlin 1.6, experimental earlier) for constructing a read-only `List` using imperative code. You give it a lambda whose **receiver** is a `MutableList<T>`, so inside the braces `this` is the mutable list and you can call `add`, `addAll`, `remove`, etc. without naming it. ```kotlin val names: List<String> = buildList { add("Ann") addAll(listOf("Bob", "Cy")) if (includeGuest) add("Guest") } ``` ## What it returns The function **returns `List<T>`** — the read-only interface — not `MutableList<T>`. The underlying object is an `ArrayList`, but it is handed back upcast to `List<T>`, so a caller holding the result cannot mutate it through that reference. The element type `T` is inferred from the `add` calls, or supplied explicitly: `buildList<Int> { add(1) }`. ## Why it exists Before `buildList`, the idiom was: ```kotlin val result = mutableListOf<String>() result.add("a") return result // leaks a MutableList, or... return result.toList() // ...needs a defensive copy ``` `buildList` removes that boilerplate and the copy: it constructs the list, lets you fill it, and seals it as read-only in one expression. ## Sibling builders - `buildSet { }` → receiver `MutableSet<T>`, returns `Set<T>`. - `buildMap { }` → receiver `MutableMap<K, V>`, returns `Map<K, V>`; you populate with `put`, `[k] = v`, or `putAll`. All three accept an optional `capacity: Int` first argument to pre-size the backing collection: `buildList(capacity = 100) { }`. ## Key terms - **Receiver lambda**: a lambda with a receiver type, written `MutableList<T>.() -> Unit`, so `this` inside it is the mutable collection. - **Read-only interface**: `List`/`Set`/`Map` expose no mutators; `MutableList` etc. add them. `buildList` returns the read-only one.

  • How do you build a map with buildMap?
    buildMap { put("a", 1); this["b"] = 2 } — the receiver is a MutableMap and it returns a read-only Map.
  • Can you specify the element type explicitly?
    Yes: buildList<String> { add("x") }, useful when the lambda is empty or the type can't be inferred.

Like an assembly line: parts move freely while the product is being built, then it ships sealed in a box you can only look at.

saying these in an interview costs you the question

  • Saying buildList returns a MutableList
  • Claiming you must call toList() at the end (the builder already seals it)
  • Thinking you call add on a variable named 'list' instead of the implicit receiver
  • Confusing buildList with listOf (listOf takes elements, not a builder lambda)

context

open as a page

Inside buildMap { }, what is 'this', and how do you add and update entries?

level: middleimportance: must knowfreq 45%

basics

~20 s

Inside the braces, 'this' is a temporary mutable map. You add entries with put or with map[key] = value, and you can overwrite existing keys the same way. When the block ends you get a read-only Map back.

open as a page

What is the capacity argument to buildList/buildSet/buildMap, and when does using a builder improve performance over a chain of operators?

level: middleimportance: should knowfreq 28%

basics

~20 s

Each builder accepts an optional initial capacity so the backing collection is pre-sized and resizes less. Builders also let you assemble a result in one pass with conditional logic, avoiding the many intermediate lists that chained operators create.

open as a page

Does buildList return a truly immutable list? What can still mutate the contents, and what does the builder actually guarantee?

level: seniorimportance: should knowfreq 35%

basics

~20 s

No. buildList returns a read-only view, not a deeply immutable list. You can't change it through the returned reference, but if you kept the mutable reference or the elements themselves are mutable objects, those can still change.

open as a page

Compare buildSet with buildList: how do add semantics and iteration order differ, and what backing collection does buildSet use?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

buildSet works like buildList but the temporary collection is a set, so adding a duplicate is ignored and add returns false. Like other Kotlin sets, it keeps elements in insertion order and returns a read-only Set.

open as a page