Lists are great when items have an order, but often you want to look something up by a name or code instead: a word's definition, a product's price by its code, a user's score by their username. A map stores key-value pairs and finds a value by its key almost instantly, however many entries there are.
A set answers a different question: "have I seen this before?" It holds unique values, quietly ignoring duplicates, which makes it perfect for tags, visited pages or the distinct cities in a list of customers.
Like lists, both come in read-only (mapOf, setOf) and mutable (mutableMapOf, mutableSetOf) versions. In this lesson you'll create, read, update and loop over maps, use them to count things, and combine sets with union and intersection.
Creating and reading maps
val prices = mapOf("tea" to 2.5, "cake" to 3.75, "soup" to 4.0)
println(prices["tea"]) // 2.5
println(prices["pizza"]) // null
println(prices.getOrDefault("pizza", 0.0)) // 0.0"tea" to 2.5creates a pair: the key and its value.map[key]returns the value, ornullif the key isn't there, so its type is nullable (Double?).getOrDefault(key, fallback), ormap[key] ?: fallback, gives a safe default.- Keys are unique; values can repeat.
"tea" in priceschecks whether a key exists.keys,valuesandsizedescribe the map.
Changing a mutable map
val stock = mutableMapOf("pen" to 300, "notebook" to 120)
stock["ruler"] = 50 // add a new entry
stock["pen"] = 280 // update an existing one
stock.remove("notebook") // remove by key
stock["pen"] = (stock["pen"] ?: 0) - 1Setting a value with map[key] = value adds the key if it's new and replaces the value if it exists. getOrPut(key) { default } returns the existing value, or adds the default and returns it, which is handy for building up maps of lists.
Looping and counting
Loop over entries and unpack each one into a key and a value:
for ((item, price) in prices) {
println("$item costs $price")
}Maps are the classic tool for counting. Start each new key at zero and add one:
val counts = mutableMapOf<String, Int>()
for (word in words) {
counts[word] = (counts[word] ?: 0) + 1
}Kotlin's groupingBy { it }.eachCount() does the same in one line, as the next lesson shows. mapOf keeps entries in the order they were inserted, so output is predictable.
Sets
val tags = mutableSetOf("kotlin", "android")
tags.add("kotlin") // ignored: already there
println(tags) // [kotlin, android]| Operation | Result |
|---|---|
a union b | Everything in either set |
a intersect b | Items in both |
a subtract b | Items in a but not b |
list.toSet() | Removes duplicates from a list |
Checking x in set is very fast, even for huge sets, which makes sets ideal for "have we seen this?" checks.
Filtering and transforming maps
Read-only maps have functions that return new maps, just like lists do. Inside the lambda, it is one entry with a key and a value:
val prices = mapOf("tea" to 2.5, "cake" to 3.75, "soup" to 4.0)
prices.filterValues { it > 3.0 } // {cake=3.75, soup=4.0}
prices.mapValues { it.value * 2 } // {tea=5.0, cake=7.5, soup=8.0}
prices.mapKeys { it.key.uppercase() } // {TEA=2.5, CAKE=3.75, SOUP=4.0}
prices.toSortedMap() // {cake=3.75, soup=4.0, tea=2.5}
prices.maxBy { it.value }.key // soup
prices + ("jam" to 1.0) // a new map with jam added
prices - "tea" // a new map without teafilterKeys works like filterValues, and plain filter gives you the whole entry when you need both parts. toSortedMap() orders the entries by key, which is handy for printing a report alphabetically.
You can also build a map from a list, without a loop:
val words = listOf("kiwi", "fig", "banana")
words.associateWith { it.length } // {kiwi=4, fig=3, banana=6}
words.associateBy { it.first() } // {k=kiwi, f=fig, b=banana}associateWith uses each item as a key and computes a value; associateBy computes a key and keeps the item as the value. If two items produce the same key, the later one wins, so associateBy silently drops data when keys clash.
When a missing key should be an error
Most of the time map[key] returning null is exactly what you want. Sometimes, though, a missing key means a bug, and you'd rather find out immediately. getValue(key) returns a non-null value or throws a NoSuchElementException with the message "Key pizza is missing in the map.".
Sets have a similar convenience: add returns true if the item was new and false if it was already there. That lets you check and record in one step:
val seen = mutableSetOf<String>()
if (!seen.add(email)) println("Duplicate: $email")Kotlin Maps Syntax
Maps and sets
val m = mapOf("a" to 1, "b" to 2)
val mm = mutableMapOf<String, Int>()
m["a"] m.getOrDefault("z", 0) "a" in m m.keys m.values
mm["x"] = 5 mm.remove("x") mm.getOrPut("y") { 0 }
for ((k, v) in m) { }
val s = setOf(1, 2, 3)
s union setOf(4) s intersect setOf(2, 9) s subtract setOf(1)
listOf(1, 1, 2).toSet()Kotlin Maps Summary
mapOf("key" to value)stores key-value pairs; keys are unique.map[key]returns a nullable value; use getOrDefault or ?: for a fallback.map[key] = valueadds or updates entries in a mutable map; remove deletes.- Loop with
for ((key, value) in map); maps are ideal for counting. - Sets hold unique values and support union, intersect and subtract.
Kotlin Maps Examples
Each example shows the code, the output it produces and what each part does.
A price list
fun main() {
val menu = mapOf("tea" to 2.5, "coffee" to 2.8, "cake" to 3.75)
val order = listOf("tea", "cake", "pizza", "tea")
var total = 0.0
for (item in order) {
val price = menu[item]
if (price == null) {
println("Sorry, no $item")
} else {
total += price
}
}
println("Total: £$total")
println("Menu has ${menu.size} items: ${menu.keys}")
}Sorry, no pizza
Total: £8.75
Menu has 3 items: [tea, coffee, cake]Line-by-line explanation
- Line 7Looking up a missing key gives null rather than crashing.
- Line 11After the null check, price is smart cast to Double.
- Line 15keys is the set of all keys, in insertion order.
Counting words
fun main() {
val text = "the cat sat on the mat the end"
val counts = mutableMapOf<String, Int>()
for (word in text.split(" ")) {
counts[word] = (counts[word] ?: 0) + 1
}
println(counts)
for ((word, n) in counts) {
if (n > 1) println("'$word' appears $n times")
}
}{the=3, cat=1, sat=1, on=1, mat=1, end=1}
'the' appears 3 timesLine-by-line explanation
- Line 5A missing word counts as 0, then 1 is added.
- Line 7Maps print as {key=value, ...}.
- Line 9Destructuring gives each entry's key and value names.
Sets of tags
fun main() {
val alice = setOf("kotlin", "android", "design")
val bob = setOf("kotlin", "ios", "design", "swift")
println("Both like: ${alice intersect bob}")
println("Anyone likes: ${alice union bob}")
println("Only Alice: ${alice subtract bob}")
val visits = listOf("home", "shop", "home", "cart", "shop")
println("Unique pages: ${visits.toSet()}")
}Both like: [kotlin, design]
Anyone likes: [kotlin, android, design, ios, swift]
Only Alice: [android]
Unique pages: [home, shop, cart]Line-by-line explanation
- Line 5intersect keeps items found in both sets.
- Line 6union combines both, without duplicates.
- Line 7subtract keeps Alice's tags that Bob doesn't have.
- Line 10toSet removes duplicates while keeping first-seen order.
Common Kotlin Maps Mistakes
Assuming a key exists
val price: Double = menu["pizza"] // Type mismatch: Double? is not Doubleval price: Double = menu["pizza"] ?: 0.0Updating a read-only map
val scores = mapOf("Ana" to 10)
scores["Ana"] = 12 // error: no set methodval scores = mutableMapOf("Ana" to 10)
scores["Ana"] = 12Using a list for membership checks on big data
val seen = mutableListOf<String>()
if (email !in seen) seen.add(email) // slower and slower as the list growsval seen = mutableSetOf<String>()
seen.add(email) // duplicates are ignored automaticallyKotlin Maps in Real Projects
Maps and sets in apps
Apps cache downloaded images in maps keyed by URL, store settings as key-value pairs, and translate interface text with maps from keys to phrases. Sets track which items a user has selected or which notifications they've already seen. Under the hood, mutableMapOf creates a LinkedHashMap and mutableSetOf a LinkedHashSet, which keep insertion order.
Kotlin Maps FAQ
How do I get a value from a map with a default in Kotlin?
Use the Elvis operator, map[key] ?: 0, or map.getOrDefault(key, 0). Both leave the map unchanged. If you also want to store the default for next time, use getOrPut(key) { 0 } on a mutable map.
Does a Kotlin map keep insertion order?
Maps from mapOf and mutableMapOf do, because they're backed by a LinkedHashMap. A hashMapOf map makes no ordering promise. If you want entries sorted by key instead, call toSortedMap() or create one with sortedMapOf().
How do I sort a map by value in Kotlin?
Maps themselves aren't sorted by value, but you can sort their entries: prices.entries.sortedBy { it.value } gives a list of entries from cheapest to dearest. Add .associate { it.toPair() } to turn the result back into a map, which keeps that order.
How do I remove duplicates from a list in Kotlin?
Call distinct() to get a new list with duplicates removed and the first occurrence of each item kept in place. toSet() does the same but gives you a Set. To remove duplicates by a property, such as email address, use distinctBy { it.email }.
What is the difference between a Map and a Set?
A Set holds unique values on their own, such as a collection of tags. A Map holds unique keys, each linked to a value, such as product names linked to prices. In fact, a map's keys property is a Set, because keys can't repeat.
Kotlin Maps Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the line so it sets the price of juice to 1.9.
prices[""] = 1.9
What does this print?
val m = mapOf("a" to 1, "b" to 2)
println(m["c"])
What does this print?
val s = mutableSetOf(1, 2, 3)
s.add(2)
s.add(4)
println(s.size)
Which expression gives every item in set a that isn't in set b?
What does this print?
val counts = mutableMapOf<Char, Int>()
for (c in "banana") counts[c] = (counts[c] ?: 0) + 1
println(counts)
Kotlin Maps Quiz
5 questions to check what you learned.
What does
"tea" to 2.5create?- A list
- A string
- A set
- A Pair, used as a map entry
What happens when you add a duplicate to a set?
- Nothing: the set keeps one copy
- An exception
- It's added twice
- The old value is removed
Which gives a value for a key, or 0 if it's missing?
- map[key]!!
- map.getOrDefault(key, 0)
- map.get(key, 0)
- map.find(key)
How do you loop over a map's keys and values?
- for (entry in map.keys)
- map.loop { }
- for ((key, value) in map)
- for (i in 0 until map.size)
Why is
x in setfaster thanx in listfor large collections?- Sets are smaller
- Sets use hashing to jump to the item, while lists check items one by one
- Lists are sorted
- It isn't faster