RizTech Academy logo
RizTech Academy
CollectionsLesson 1 of 525 min

Lists, sets and maps

Collections are the containers you keep data in — a list of orders, a set of tags, a map from pincode to city. You will touch them in every program you write, so it is worth knowing not just their API but what each one is for and what it costs. This lesson introduces the three you will use daily: List, Set, and Map.

List — an ordered sequence

A list holds items in order, allows duplicates, and is accessed by position (index):

val cities = listOf("Pune", "Mumbai", "Nagpur", "Pune")
println(cities[0])            // Pune          — indexing, from zero
println(cities.first())      // Pune
println(cities.last())       // Pune
println(cities.size)         // 4             — duplicates kept
println(cities.indexOf("Nagpur"))  // 2
println("Mumbai" in cities)  // true          — membership test

listOf(...) creates a read-only list (the next lesson is about read-only versus mutable). A list is the right choice when order matters or duplicates are allowed — a sequence of events, a queue of tasks, the lines of a file.

The cost worth knowing: indexing (list[i]) is instant, and so is adding to the end. But "x" in list — checking membership — scans the list item by item, so it is slow on a large list. If you find yourself repeatedly asking "is this in the list?", a Set is the right tool, and this is the single most common collection performance mistake.

Set — unique items, fast membership

A set holds unique items — duplicates are silently ignored — and is built for one question: "is this in here?"

val tags = setOf("fiction", "literary", "fiction", "classic")
println(tags)                // [fiction, literary, classic]  — the duplicate gone
println(tags.size)           // 3
println("fiction" in tags)   // true   — this is FAST, even on a huge set

Adding a duplicate does nothing; membership testing is effectively instant regardless of size (a hash lookup, not a scan). And sets do set algebra:

val a = setOf(1, 2, 3, 4)
val b = setOf(3, 4, 5, 6)
println(a intersect b)       // [3, 4]        — in both
println(a union b)           // [1, 2, 3, 4, 5, 6]   — in either
println(a subtract b)        // [1, 2]        — in a but not b

Reach for a Set when you need uniqueness ("the distinct tags"), or fast membership ("have I seen this user id?"). The trade-off: a set does not preserve insertion order by default (use LinkedHashSet, which setOf actually gives you, if order matters — Kotlin's setOf does keep insertion order, but do not rely on ordering as a set's purpose).

Map — keys to values

A map associates each key with a value — a dictionary, a lookup table:

val cityByPincode = mapOf(
    "411038" to "Pune",
    "400001" to "Mumbai",
    "440001" to "Nagpur"
)
println(cityByPincode["411038"])        // Pune
println(cityByPincode["999999"])        // null   — a missing key gives null
println(cityByPincode.getOrDefault("999999", "Unknown"))  // Unknown
println("411038" in cityByPincode)      // true   — 'in' checks the KEYS
println(cityByPincode.keys)             // [411038, 400001, 440001]
println(cityByPincode.values)           // [Pune, Mumbai, Nagpur]

The "key" to "value" syntax creates a Pair, and mapOf builds a map from pairs. Two things to internalise:

  • Looking up a missing key returns null, not an error — so map[key] has type Value?. This is the null-safety module showing up again: a map lookup might find nothing, and the type says so. Use getOrDefault or Elvis (map[key] ?: default) to handle the miss.
  • Keys are unique; assigning a key twice keeps the last value. Lookup by key is fast (a hash lookup, like a set), which is the whole point — a map is what you use when you need to find a value by something quickly.

Iterating a map gives you entries you can destructure:

for ((pincode, city) in cityByPincode) {
    println("$pincode -> $city")
}

That (pincode, city) is the destructuring you met with withIndex — a map entry unpacks into key and value.

Choosing the right one

The decision is almost always clear once you name what you are doing:

You need… Use Why
An ordered sequence, duplicates allowed List Order and position matter
Unique items, or fast "is this present?" Set Uniqueness and instant membership
To find a value by a key Map Fast key lookup

The mistake to avoid: using a List and repeatedly scanning it with in or indexOf when you actually needed a Set (for membership) or a Map (for lookup). On small collections it does not matter; on large ones it is the difference between instant and sluggish. Pick the structure from what you will do with it, not from what feels familiar.

Check your work

What a List is for. An ordered sequence where duplicates are allowed and position matters.

The List cost to remember. Indexing and appending are instant; in/indexOf scan the list, so they are slow on large lists.

What a Set is for. Unique items and fast membership testing.

How a Set handles duplicates and ordering. Duplicates are ignored; ordering is not its purpose (though setOf happens to preserve insertion order).

Three set operations. intersect, union, subtract.

What a Map associates, and how lookup performs. Keys to values; key lookup is fast (a hash lookup).

What a missing map key returns. null — so map[key] is nullable; use getOrDefault or Elvis.

What in checks on a map. The keys.

The common collection mistake. Using a List and scanning it with in/indexOf when a Set or Map was the right structure.

Practice

  1. Make a listOf with a duplicate and confirm the size counts it. Index the first and last elements.
  2. Make a setOf with a duplicate and confirm it is dropped. Test membership with in.
  3. Compute intersect, union, and subtract of two sets.
  4. Build a mapOf from pincode to city. Look up a present key and a missing one; handle the miss with ?: "Unknown".
  5. Confirm map[missingKey] has a nullable type (assign it to a non-null String and read the error).
  6. Iterate the map with destructuring for ((k, v) in map).
  7. Given a task "check if each of 10,000 ids has been seen before", decide whether a List or a Set is correct, and explain why in terms of cost.

Official documentation

Next: read-only versus mutable collections — one of Kotlin's most important distinctions.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship