Lists, Sets & Maps

Kotlin · Lesson 12 of 29 6 min read Updated

A single variable holds one value. Real programs handle many: all the items in a shopping cart, every student in a class, the stock count of each product. Kotlin gives you three main collections for this: lists, sets and maps.

Each comes in two flavours: a read-only version that cannot be changed after it is created, and a mutable version that can. In this lesson you will create and use all of them, and learn which one to choose.

The three collection types

CollectionWhat it holdsRead-onlyMutable
Listitems in order, duplicates allowedlistOf(...)mutableListOf(...)
Setunique items, no duplicatessetOf(...)mutableSetOf(...)
Mapkey-value pairs, unique keysmapOf(k to v)mutableMapOf(k to v)

As with val and var, prefer the read-only versions and use mutable ones only when the contents really need to change.

Lists

A list keeps items in order. Each item has an index, starting at 0.

Kotlin
fun main() {
    val cities = listOf("Rohtak", "Jaipur", "Pune", "Kochi")

    println(cities)
    println(cities.size)
    println(cities[1])
    println(cities.first() + " to " + cities.last())
    println("Pune" in cities)
    println(cities.indexOf("Kochi"))
    println(cities.getOrNull(10))
    println(cities.subList(1, 3))
}
Output
[Rohtak, Jaipur, Pune, Kochi]
4
Jaipur
Rohtak to Kochi
true
3
null
[Jaipur, Pune]

Printing a list shows its items in square brackets. cities[10] would crash with an IndexOutOfBoundsException, so getOrNull(10) is the safe choice when an index might be invalid. subList(1, 3) takes indexes 1 and 2.

A read-only List has no add or remove functions at all, so you cannot change it by mistake.

Mutable lists

A MutableList adds functions that change it: add, remove, removeAt, clear, and assignment by index.

Kotlin
fun main() {
    val cart = mutableListOf("Rice", "Dal")
    cart.add("Ghee")
    cart.add(0, "Atta")
    cart += "Salt"
    println(cart)

    cart.remove("Dal")
    cart[1] = "Basmati Rice"
    println(cart)

    cart.removeAt(cart.lastIndex)
    println(cart)
    println(cart.size)
}
Output
[Atta, Rice, Dal, Ghee, Salt]
[Atta, Basmati Rice, Ghee, Salt]
[Atta, Basmati Rice, Ghee]
3

Notice that cart is a val, yet its contents change. val only means the variable always points to the same list; the list itself is mutable. add(0, "Atta") inserts at the front, and cart += "Salt" is a short way to write cart.add("Salt").

Kotlin also has handy ways to build lists:

Kotlin
fun main() {
    val squares = List(5) { it * it }
    println(squares)

    val seats = MutableList(3) { "free" }
    seats[1] = "booked"
    println(seats)

    val isWeekend = true
    val menu = buildList {
        add("Idli")
        add("Dosa")
        if (isWeekend) add("Pongal")
    }
    println(menu)
}
Output
[0, 1, 4, 9, 16]
[free, booked, free]
[Idli, Dosa, Pongal]

List(5) { it * it } creates five items, calling the lambda with each index. buildList lets you add items step by step, with conditions, and gives back a read-only list at the end.

Sets

A set keeps each value only once. Adding a value that is already there does nothing. Sets are ideal for removing duplicates and for fast "is this in the group?" checks. Kotlin's default sets remember the order in which items were added.

Kotlin
fun main() {
    val visitors = listOf("Aarav", "Isha", "Aarav", "Kabir", "Isha")
    val unique = visitors.toSet()
    println(unique)
    println(unique.size)

    val cricket = setOf("Aarav", "Kabir", "Rohan")
    val chess = setOf("Kabir", "Meera", "Rohan")
    println(cricket intersect chess)
    println(cricket union chess)
    println(cricket subtract chess)

    val tags = mutableSetOf("kotlin")
    println(tags.add("android"))
    println(tags.add("kotlin"))
    println(tags)
}
Output
[Aarav, Isha, Kabir]
3
[Kabir, Rohan]
[Aarav, Kabir, Rohan, Meera]
[Aarav]
true
false
[kotlin, android]

intersect finds students who play both games, union combines everyone without repeats, and subtract finds those who play only cricket. add returns true if the item was new and false if it was already present.

Maps

A map stores key-value pairs, like a phone book (name to number) or a price list (item to price). Each key appears once. You create pairs with the word to, and look up values with square brackets.

Kotlin
fun main() {
    val stock = mutableMapOf("pen" to 40, "notebook" to 25)
    stock["eraser"] = 60
    stock["pen"] = 35
    println(stock)

    println(stock["notebook"])
    println(stock["stapler"])
    println(stock.getOrDefault("stapler", 0))
    println(stock.keys)
    println(stock.values.sum())

    for ((item, qty) in stock) {
        println("$item: $qty")
    }

    stock.remove("eraser")
    println("eraser" in stock)
}
Output
{pen=35, notebook=25, eraser=60}
25
null
0
[pen, notebook, eraser]
120
pen: 35
notebook: 25
eraser: 60
false

Important details:

  • Printing a map shows {key=value, ...}.
  • stock["stapler"] returns null for a missing key, so the result type is Int?. Use getOrDefault, ?: 0, or getValue (which throws an exception if the key is missing).
  • Assigning to an existing key, like stock["pen"] = 35, replaces the old value.
  • for ((item, qty) in stock) unpacks each entry into its key and value.

Counting with a map, and Pair

A very common job is counting how often something appears. "Aarav" to 91 creates a Pair, a small object holding two values called first and second.

Kotlin
fun main() {
    val votes = listOf("Biryani", "Dosa", "Biryani", "Chole", "Dosa", "Biryani")
    val count = mutableMapOf<String, Int>()
    for (dish in votes) {
        count[dish] = (count[dish] ?: 0) + 1
    }
    println(count)

    val topper = "Aarav" to 91
    println(topper)
    println(topper.first)
}
Output
{Biryani=3, Dosa=2, Chole=1}
(Aarav, 91)
Aarav

mutableMapOf<String, Int>() creates an empty map; because it starts empty, you write the key and value types in angle brackets. In the next lesson you will see that votes.groupingBy { it }.eachCount() does this counting in one line.

Common mistakes

  • Expecting listOf to allow add. Use mutableListOf when the list must change, or create a new list with list + item.
  • Thinking read-only means frozen. A read-only List view can point to a list that other code still changes. If you need a snapshot, copy it with toList().
  • Forgetting that map lookups can be null. stock["pen"] + 1 does not compile; handle the null first.
  • Using a list for membership checks on big data. in on a list checks every item; a set answers much faster.

Key points

  • Lists are ordered and allow duplicates; sets keep unique values; maps store key-value pairs.
  • listOf, setOf and mapOf create read-only collections; the mutable...Of versions can change.
  • val with a mutable collection fixes the reference, not the contents.
  • list[i], getOrNull, first(), last() and in read lists safely.
  • map[key] returns a nullable value; to creates a Pair for map entries.
  • Arrays exist but lists are the everyday choice; print arrays with contentToString().

Exercise

A school canteen recorded the snacks sold in one break: "samosa", "juice", "samosa", "sandwich", "juice", "samosa". Prices are samosa Rs 15, juice Rs 20 and sandwich Rs 30, stored in a map. Count how many of each snack were sold, print the counts, then print the total earnings by looking up each snack's price.

Show solution
Kotlin
fun main() {
    val sold = listOf("samosa", "juice", "samosa", "sandwich", "juice", "samosa")
    val prices = mapOf("samosa" to 15, "juice" to 20, "sandwich" to 30)

    val counts = mutableMapOf<String, Int>()
    for (snack in sold) {
        counts[snack] = (counts[snack] ?: 0) + 1
    }
    println(counts)

    var total = 0
    for ((snack, qty) in counts) {
        total += qty * prices.getValue(snack)
    }
    println("Total: Rs $total")
}
Output
{samosa=3, juice=2, sandwich=1}
Total: Rs 115

getValue returns a plain Int because every snack sold has a price; if one were missing, it would throw an exception instead of silently using null.

Quick Quiz

1. Which collection automatically removes duplicates?

2. What does mapOf("a" to 1)["b"] return?

3. val names = mutableListOf("Isha"). Is names.add("Rohan") allowed?

4. How do you print the items of an IntArray called a?

Learn Kotlin with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call