Maps and Sets
A list keeps things in order. A map looks things up by name, and a set refuses duplicates. After this you will know which of the three to reach for, and how to walk through a map's entries.
When a list is the wrong shape
You are keeping score for a game night. Four players, four scores. A can hold them:
1val names = listOf("Ada", "Alan", "Grace", "Zoe")
2val points = listOf(12, 9, 15, 4)Now answer one question: how many points does Grace have?
You have to find Grace's in the first list, then read the same index out of the second one. Two steps, two lists, and one gruesome bug waiting to happen — if you ever add a name without adding a score, every answer after Grace becomes wrong for ever.
The problem is not your code. The problem is that a list is the wrong shape for this job. You do not want positions. You want lookups.
Think about the cloakroom at a theatre.
You hand over your coat and get a numbered ticket. Later you hand back ticket 47 and the attendant fetches coat 47. Nobody counts along the rail from the left-hand end. The ticket is the way in.
That is a : a ticket () that leads straight to one thing ().
Now think about the guest list on the door of the same theatre. Writing "Ada" on it twice does not let Ada in twice. The list only records whether a name is on it.
That is a : no positions, no duplicates, just membership.
A map is a lookup table
1val scores = mapOf("Ada" to 12, "Grace" to 15)
2println(scores["Grace"])
3println(scores["Zoe"])That prints:
115
2nullThree new things are happening here.
mapOf builds a map. Every entry is written as key to value. That little word to is real Kotlin — it builds a , which is just two values glued together.
Square brackets look things up. scores["Grace"] does not mean "position Grace". It means "the value filed under Grace".
A missing key gives you . Not a crash, not zero — null, which is Kotlin's word for there is nothing here. If you want a stand-in instead, the operator supplies one:
println(scores["Zoe"] ?: 0)That prints 0. Read ?: as "or else". Lesson 1.13 is entirely about null and this operator, so for now just take it as a handy fallback.
Keys are unique; values are not
Two players can both have fifteen points. No two entries can share a key. If you write the same key twice, the later one silently wins:
1val m = mapOf("Ada" to 12, "Ada" to 15)
2println(m.size)
3println(m["Ada"])That prints 1 then 15. There is only ever one Ada slot.
Walking through every entry
A for loop over a map hands you both halves at once:
1for ((name, score) in scores) {
2 println("$name scored $score")
3}The double brackets around (name, score) are — unpacking one entry into two names in a single move. Lesson 1.15 uses the same trick on your own types.
If you only want one half, scores.keys and scores.values give you each side on its own.
Changing a map
mapOf builds a read-only map. To add and remove entries you want a one:
1val scores = mutableMapOf("Ada" to 12)
2scores["Zoe"] = 4 // add
3scores["Ada"] = 15 // replace
4scores.remove("Zoe") // delete
5println(scores.containsKey("Ada"))
6println("Zoe" in scores)That prints true then false. Note that in works on a map's keys, and reads almost like English.
val scores = mutableMapOf(...) is not a contradiction. The means this name will always point at this same map. The word mutable means the contents of that map can change. Locked label, open box.
A set holds each thing once
1val tags = setOf("red", "blue", "red")
2println(tags.size)
3println("blue" in tags)That prints 2 then true. The second "red" was simply absorbed.
A set has no positions, so tags[0] is not a thing you can write — the compiler rejects it. In exchange you get two superpowers:
- Instant membership tests.
"blue" in tagsis fast even with a hundred thousand tags. - Free de-duplication.
.toSet()on any throws the repeats away.
1val rolls = listOf(6, 4, 6, 2, 4)
2println(rolls.toSet())That prints [6, 4, 2] — each face once, in the order it was first seen.
mutableSetOf gives you add and remove. add hands back true if the item was new and false if it was already there, which is a tidy way to ask "have I seen this before?"
Which one when
| What you need | Reach for |
|---|---|
| Things in order, duplicates allowed, read by position | List |
| Look a value up by a name or an id | Map |
| A membership list where duplicates are meaningless | Set |
You will use all three in Part 5. Pocket Notes stores notes in a list because order matters. Focus Flow looks up a session's settings by name in a map. And a "which days did I focus?" question is a set, because a day either counts or it does not.
- Open Pocket Studio and tap Projects.
- Open your Kotlin practice project — the one you have used since Lesson 1.1.
- Tap Editor and open
Playground.kt. - Select everything in the file and delete it.
- Type the ten-line program from the walkthrough above, exactly as written.
- Tap Run (the triangle) and watch the Output panel.
You should see exactly this:
1Ada: 15
2Alan: 9
3Players: 2
4Faces seen: [6, 4, 2]- Now change line 4 from
scores["Ada"] = 15toscores["Zoe"] = 15and run again. Players:becomes3, and Ada is back on twelve points. You added a key instead of replacing one — proof that the brackets do both jobs depending on whether the key exists.
mapOf rather than mutableMapOf. A read-only map can be read with brackets but never written to.mapOf(...) to mutableMapOf(...). The val in front can stay exactly as it is.scores["Ada"] = "15" with quotes around the number.scores["Ada"] = 15. Quotes make a ; no quotes makes an .Int? — a number or null. You asked to store that in a plain Int, which is not allowed to be null.?:, as in val n: Int = scores["Zoe"] ?: 0. Lesson 1.13 covers why Kotlin is so strict about this.add on something built by setOf or listOf. Those are read-only, so they have no add at all — the name does not exist to be called.mutableSetOf or mutableListOf instead.tags[0] on a set. Sets have no positions, so there is nothing for a number in brackets to mean."red" in tags.- A stores and pairs and looks things up by key, not by position. Keys are unique.
- Looking up a missing key gives ;
?:supplies a fallback. for ((k, v) in map)walks every entry, unpacking both halves at once.- A holds each item once.
intests membership, and.toSet()strips duplicates out of any collection. mapOfandsetOfare read-only;mutableMapOfandmutableSetOfcan be changed.- Next: the collection functions that replace most of your loops —
filter,mapandsumOfturn ten lines into one.