skip to content

Collection Types & Builders

Which collection interfaces exist and how you actually create instances: factory functions, builder blocks, and the array types that sit alongside them. Interviewers open the collections topic here before moving to operators.

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

explore

questions

30

How do you create an array in Kotlin and read/write its elements?

level: juniorimportance: must knowfreq 70%

answer

  1. arrayOf(...) and Array(size) { it }
  2. [] = get/set, zero-based
  3. fixed size, no add/remove
  4. size, lastIndex, indices
  5. val locks reference, not elements

basics

~10 s

Use arrayOf(1, 2, 3) to make one, then read or change an element with square brackets like a[0] or a[0] = 9. Get the length with a.size.

solid answer

~30 s

Create arrays with the factory functions: arrayOf("a", "b") for an Array<String>, or the Array(size) { index -> ... } constructor that fills each slot from a lambda. Access uses the indexing operator [], which compiles to get(index)/set(index, value). Out-of-bounds access throws IndexOutOfBoundsException. The number of elements is a.size, indices run 0 until size. Arrays have a FIXED size after creation — there is no add/remove. Useful helpers: arrayOfNulls<T>(n) (all null, type Array<T?>), emptyArray<T>(), and lastIndex. Unlike a List, an array's contents are always mutable in place even though the reference can be a val.

code

kotlin · 5 lines
kotlin
val a = Array(3) { it + 1 }   // [1, 2, 3]
a[2] = 99                      // set
println(a[2])                  // 99
println(a.size)                // 3
// a[3] -> IndexOutOfBoundsException

go deeper

for a junior

Creates with arrayOf, reads/writes with [], knows .size and zero-based indexing.

for a middle

Reaches for the Array(size) { } constructor and arrayOfNulls/emptyArray, and explains the fixed-size nature.

for a senior

Articulates the get/set operator desugaring, val-vs-contents mutability, and when to choose arrays over List.

for a principal

Frames arrays as low-level interop/perf tools, steering teams toward List by default and reserving arrays for varargs, buffers, and primitive performance.

## What an array is A Kotlin **array** is a fixed-size, indexable container of elements of one type, represented by the `Array<T>` class (which maps to a JVM object array). Once created its **length never changes** — you can replace elements but not add or remove them. ## Creating arrays - **`arrayOf(...)`** — vararg factory: `val a = arrayOf(1, 2, 3)` infers `Array<Int>`. - **`Array(size) { index -> value }`** — the constructor that computes each element from its index: `val sq = Array(5) { it * it }` gives `[0, 1, 4, 9, 16]`. - **`arrayOfNulls<String>(3)`** — an `Array<String?>` filled with `null`. - **`emptyArray<Int>()`** — a zero-length array. ```kotlin val names = arrayOf("Ann", "Bo") val table = Array(3) { i -> i * 10 } // [0, 10, 20] names[0] = "Cy" // set println(names[1]) // get -> "Bo" println(names.size) // 2 println(names.lastIndex) // 1 ``` ## Indexing The `[]` syntax is the **indexing operator**: `a[i]` calls `a.get(i)` and `a[i] = x` calls `a.set(i, x)`. Indices are zero-based, valid range `0 until size`. Reading or writing outside that range throws **`IndexOutOfBoundsException`**. ## Size and iteration - `a.size` — element count; `a.lastIndex` — `size - 1`. - `a.indices` — the `IntRange` `0..lastIndex`. - Iterate with `for (x in a)`, `for (i in a.indices)`, or `a.forEachIndexed { i, x -> ... }`. ## val vs contents A `val a = arrayOf(...)` makes the **reference** immutable, but the **elements are still mutable** (`a[0] = ...` is legal). Arrays are not read-only the way `List` is. ## When to use Prefer `List`/`MutableList` for most code (richer API, safer). Reach for arrays for interop, fixed-size buffers, varargs (`vararg` parameters are arrays), or primitive-array performance (see `IntArray`).

  • If I declare the array with val, can I still change its elements?
    Yes. val only freezes the reference; a[i] = x still works because the array's contents are mutable.
  • How is Array(n) { ... } different from arrayOfNulls<T>(n)?
    Array(n) { ... } runs the lambda to initialize every slot to a real value; arrayOfNulls fills all slots with null and gives type Array<T?>.

An array is like a row of numbered mailboxes: a fixed count of slots you can refill, but you cannot bolt on a new mailbox.

saying these in an interview costs you the question

  • Thinking arrays can grow with add()/remove()
  • Believing val makes elements unmodifiable
  • Confusing size (a property) with a length() method
  • Expecting one-based indexing
  • Not knowing out-of-bounds throws IndexOutOfBoundsException

context

open as a page

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

level: juniorimportance: must knowfreq 55%

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.

open as a page

How do you create a collection in Kotlin using factory functions like listOf, setOf, and mapOf, and what do they return?

level: juniorimportance: must knowfreq 80%

basics

~10 s

Use listOf(1, 2, 3), setOf(...), or mapOf("a" to 1) to build collections quickly. They give you read-only collections you cannot add to or remove from.

open as a page

What does map[key] return in Kotlin when the key is absent, and how is that different from map.getValue(key)?

level: juniorimportance: must knowfreq 65%

basics

~10 s

map[key] gives you null when the key isn't there, so the result type is nullable. map.getValue(key) instead throws an exception if the key is missing, returning a non-null value when it exists.

open as a page

When you call mapOf(...) or setOf(...) in Kotlin, what concrete JVM type backs the result, and what does that mean for iteration order?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Kotlin's default maps and sets keep elements in the order you inserted them. They are backed by LinkedHashMap and LinkedHashSet, so looping over them gives you items in insertion order.

open as a page

In Kotlin, what is the difference between List and MutableList (and Set/MutableSet, Map/MutableMap)?

level: juniorimportance: must knowfreq 85%

basics

~10 s

List only lets you read items. MutableList lets you also add, remove, or change them. The same idea applies to Set/MutableSet and Map/MutableMap.

open as a page

In Kotlin, what does it mean that a `List` is read-only, and why is that not the same as being immutable?

level: juniorimportance: must knowfreq 70%

basics

~10 s

A read-only List only lacks add/remove methods on its interface. The actual object behind it can still change through another reference. Read-only means 'you can't change it', not 'nobody can'.

open as a page

What is the difference between Array<Int> and IntArray in Kotlin, and why does it matter?

level: middleimportance: must knowfreq 75%

basics

~10 s

Array<Int> stores boxed Integer objects; IntArray stores raw int values directly. IntArray uses less memory and is faster because it skips the boxing, so prefer it for numbers.

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 difference between listOf and mutableListOf (and the other mutable* factories), and when would you use each?

level: middleimportance: must knowfreq 75%

basics

~10 s

listOf gives a read-only list; mutableListOf gives one you can add to and remove from. Use mutable only when you actually need to change the collection after creating it.

open as a page

Compare getOrDefault, getOrElse, and getOrPut on a Kotlin Map. When does each evaluate its fallback, and which one mutates the map?

level: middleimportance: must knowfreq 60%

basics

~20 s

All three give a value when a key is missing. getOrDefault takes a ready value. getOrElse runs a function only when needed. getOrPut also runs a function when needed but stores the result back into the map.

open as a page

You return a `List<T>` from a function but it's backed by a private `MutableList<T>` field. What is the aliasing risk and how do `toList()` / `toMutableList()` defend against it?

level: middleimportance: must knowfreq 60%

basics

~20 s

If you return the same backing list, the caller's read-only reference still changes whenever your class mutates the field. Returning field.toList() makes a copy, so the caller gets a snapshot that can't change underneath them.

open as a page

What contract must an element type satisfy for a Kotlin Set (or Map key) to deduplicate correctly, and what breaks if you use a mutable object as a key?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Sets and map keys decide 'same element' using equals() and hashCode(). If two objects are equal they must have the same hashCode. If you change a key after inserting it, the set can no longer find it.

open as a page

Walk through the conversions between Array<T>, primitive arrays, and collections (toTypedArray, toIntArray, toList, etc.).

level: middleimportance: should knowfreq 60%

basics

~10 s

Use toTypedArray() to get an Array<T>, toIntArray()/toLongArray() for primitive arrays, and toList()/toMutableList() for collections. Each one copies the data into a new container.

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

When and why would you use emptyList()/emptySet()/emptyMap() and listOfNotNull() instead of plain listOf?

level: middleimportance: should knowfreq 55%

basics

~10 s

emptyList() gives a typed empty collection (often a shared singleton). listOfNotNull(a, b, c) builds a list but skips any null arguments, so you do not get nulls in the result.

open as a page

Why is List<out E> declared covariant but MutableList<E> invariant in Kotlin?

level: middleimportance: should knowfreq 55%

basics

~10 s

A read-only List<Cat> can safely be treated as a List<Animal> because you only read from it. A MutableList<Cat> cannot, because you could try to add a Dog, so it must stay exactly its type.

open as a page

What happens at runtime when you cast a `List<T>` returned by `listOf(...)` back to `MutableList<T>` and call `add`? Contrast that with a list created by `mutableListOf(...)`.

level: middleimportance: should knowfreq 45%

basics

~20 s

It depends on the real object. listOf may give an unmodifiable instance that throws when you try to add. mutableListOf gives a real ArrayList, so the cast-and-add succeeds. Both compile, but casting back is unsafe.

open as a page

Why does == on two arrays with equal contents return false, and how do you compare arrays correctly?

level: seniorimportance: should knowfreq 55%

basics

~10 s

For arrays, == checks identity (same object), not contents, so two different arrays with the same values are not equal. Use contentEquals() to compare values, and contentHashCode()/contentToString() for hashing and printing.

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

Explain the 'to' infix function used in mapOf. What does it produce, and what are its pitfalls?

level: seniorimportance: should knowfreq 50%

basics

~10 s

'to' is a small function that builds a key-value Pair, like "a" to 1. mapOf takes those pairs. It is just a Pair, so it has no special map magic.

open as a page

How should the read-only vs mutable interface split inform the types you choose for function parameters, return values, and class properties?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Use the read-only type (List, Set, Map) almost everywhere — for inputs you only read and for things you return. Only use the Mutable type when the function or caller really needs to change the collection.

open as a page

A function returns List<String>, yet a caller manages to mutate it at runtime. How is that possible, and how do the read-only interfaces relate to the actual JVM collection classes?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The read-only type only hides change methods at compile time. The real object underneath is often a normal mutable ArrayList, so anyone holding a mutable reference to it, or a cast, can still change it.

open as a page

How does Java interop affect Kotlin's read-only guarantee? If a Kotlin `List<T>` is passed to Java (or comes from Java), what can go wrong?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Java doesn't know about Kotlin's read-only vs mutable split. A Kotlin List is a plain java.util.List to Java code, which can call add/set on it. And collections coming from Java arrive as platform types Kotlin can't fully police.

open as a page

Is getOrPut on a plain mutableMapOf safe under concurrent access, and what is the correct alternative if you need atomic lazy initialization?

level: principalimportance: should knowfreq 30%

basics

~10 s

No. getOrPut on a normal map is not thread-safe: two threads can both see a missing key and both compute and store a value. For safe concurrent lazy init use a concurrent map's computeIfAbsent.

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

How does type inference work for factory functions like listOf, and what surprises arise with mixed types or spreading an array?

level: seniorimportance: nice to knowfreq 35%

basics

~10 s

Kotlin guesses the element type from the arguments you pass. Mixing types gives a common supertype like Any. You can pass an existing array into the vararg using the spread operator *.

open as a page

Why can sharing a read-only `List` view across threads still be unsafe, and how do snapshot copies or immutable/persistent collections change the picture?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

A read-only view doesn't stop the underlying list from being changed by another thread. One thread can iterate while another mutates the backing list, causing inconsistent reads or exceptions. A copy or a truly immutable list avoids this because nothing can change it.

open as a page

Explain array variance in Kotlin: why is Array<T> invariant, and how do primitive arrays and varargs relate to Java's covariant arrays?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

In Kotlin you cannot pass an Array<String> where an Array<Any> is expected — arrays are invariant. This prevents a class of runtime errors that Java's covariant arrays allow.

open as a page

Kotlin has no 'immutable' keyword or a frozen List type in the stdlib. Given only List/MutableList, how do you reason about and achieve actual immutability, and where do the standard interfaces stop?

level: principalimportance: nice to knowfreq 25%

basics

~10 s

The List type only hides change methods; it is not a promise of immutability. To truly prevent changes you copy the data, keep the original hidden, or use a special immutable collection library.

open as a page