Kotlin Lists

Store ordered items in read-only and mutable lists, read them by index, add, remove and update items, and use the most common list functions.

Lesson 9 of 43Beginner18 min read

Most real data comes in groups: the songs in a playlist, the messages in a chat, the items in a shopping basket, the scores in a game. A list stores items in order, so you can read them by position, loop over them, and add or remove items as things change.

Kotlin's collections have a feature that surprises newcomers: lists come in two flavours. listOf creates a read-only list that can't be changed after it's made, and mutableListOf creates one you can add to and remove from. Separating the two makes code safer, because a function that receives a read-only list can't accidentally change your data.

In this lesson you'll create both kinds, read items by index, change mutable lists, and use the built-in functions for searching, sorting and slicing. Later lessons build on this with maps and sets and powerful collection operations.

Creating and reading lists

Kotlin
val colours = listOf("red", "green", "blue")
println(colours[0])          // red
println(colours.size)        // 3
println(colours.last())      // blue
  • Items are numbered from 0, so the last index is size - 1.
  • first() and last() read the ends; getOrNull(5) returns null instead of crashing when the index doesn't exist.
  • isEmpty() and isNotEmpty() check whether there are any items.
  • Lists print neatly: [red, green, blue].
  • A list's type says what it holds: List<String>, List<Int>. Kotlin infers it from the items.

Mutable lists

Kotlin
val basket = mutableListOf("apples", "bread")
basket.add("milk")              // at the end
basket.add(0, "eggs")           // at position 0
basket[1] = "green apples"      // replace
basket.remove("bread")          // by value
basket.removeAt(0)              // by position

Note that basket is a val, yet the list changes. val means the variable always points to the same list; the list's contents can still change because it's mutable. To start with an empty mutable list, give its type: mutableListOf<String>().

Searching, sorting and slicing

FunctionReturns
contains(x) or x in listtrue if the item is present
indexOf(x)its position, or -1
sorted() / sortedDescending()a new sorted list
reversed()a new list in reverse order
take(n) / takeLast(n)the first or last n items
subList(from, to)items from from up to, not including, to
sum(), average(), max(), min()for lists of numbers

On a mutable list, sort() and reverse() change the list in place, while sorted() and reversed() always return new lists.

Lists and arrays

Kotlin also has arrays, created with arrayOf(1, 2, 3) or IntArray(5). Arrays have a fixed size: you can change items but not add or remove them. They're mainly used for performance-critical code and when working with Java libraries. For everyday programming, lists are more flexible and have more functions, so they're the usual choice. Note that arrays don't print nicely; use array.contentToString() to see their items.

Building new lists without a mutable one

You don't always need a MutableList to put a list together. Kotlin has several ways to build a read-only list in one step:

Kotlin
val squares = List(5) { i -> i * i }          // [0, 1, 4, 9, 16]
val names = listOf("Ana", "Ben")
val more = names + "Cy"                        // [Ana, Ben, Cy]
val fewer = more - "Ana"                       // [Ben, Cy]
val evens = buildList {
    for (i in 1..10) if (i % 2 == 0) add(i)
}                                              // [2, 4, 6, 8, 10]
  • List(size) { index -> value } creates a list by running the block once for each position.
  • + and - return a new list. The original names is still [Ana, Ben] afterwards.
  • buildList { } lets you call add inside the block, then hands back a read-only list when the block ends.

With a var, you can even write tags += "b". That doesn't change a list; it builds a new one and points tags at it. This style is common in apps that keep their data immutable.

To get a list you're allowed to change, make a copy with toMutableList(). The copy is completely separate: adding "Zed" to it leaves the original untouched.

Two lists are equal with == when they hold the same items in the same order, whatever kind of list they are, so listOf(1, 2) == mutableListOf(1, 2) is true. Order matters, though: listOf(1, 2) == listOf(2, 1) is false.

Kotlin Lists Syntax

List essentials

val list = listOf("a", "b", "c")
val names = mutableListOf<String>()
list[0]  list.first()  list.last()  list.getOrNull(9)
list.size  list.isEmpty()  "b" in list  list.indexOf("b")
names.add("x")  names.add(0, "y")  names.remove("x")  names.removeAt(0)
list.sorted()  list.reversed()  list.take(2)  list.subList(1, 3)
listOf(3, 1, 2).sum()

Kotlin Lists Summary

  • listOf makes a read-only list; mutableListOf makes one you can change.
  • Indexes start at 0; use first, last and getOrNull to read safely.
  • add, remove, removeAt and list[i] = x change a mutable list.
  • sorted, reversed, take and subList return new lists; sort and reverse change in place.
  • Prefer lists to arrays for everyday code.

Kotlin Lists Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A to-do list

Kotlin
fun main() {
    val todo = mutableListOf("Buy milk", "Walk the dog", "Finish homework")
    todo.add(1, "Call Grandma")
    println(todo)

    todo.remove("Walk the dog")
    todo[0] = "Buy oat milk"

    for ((i, task) in todo.withIndex()) {
        println("${i + 1}. $task")
    }
    println("Homework on the list? ${"Finish homework" in todo}")
}
Output
[Buy milk, Call Grandma, Walk the dog, Finish homework]
1. Buy oat milk
2. Call Grandma
3. Finish homework
Homework on the list? true
Line-by-line explanation
  1. Line 3add with a position inserts there and shifts the rest along.
  2. Lines 6 to 7Remove by value, then replace the first item.
  3. Line 12in checks whether the list contains the item.
Example 2

Numbers, sorting and slicing

Kotlin
fun main() {
    val scores = listOf(72, 95, 58, 88, 64)
    println("Highest: ${scores.max()}, lowest: ${scores.min()}")
    println("Average: ${scores.average()}")

    val ranked = scores.sortedDescending()
    println("Ranked: $ranked")
    println("Top 3: ${ranked.take(3)}")
    println("Original: $scores")
    println(scores.getOrNull(10) ?: "No 11th score")
}
Output
Highest: 95, lowest: 58
Average: 75.4
Ranked: [95, 88, 72, 64, 58]
Top 3: [95, 88, 72]
Original: [72, 95, 58, 88, 64]
No 11th score
Line-by-line explanation
  1. Lines 3 to 4Number lists have max, min, sum and average built in.
  2. Line 6sortedDescending returns a new list.
  3. Line 9The original list is unchanged.
  4. Line 10getOrNull avoids a crash for a missing index.
Example 3

Read-only outside, mutable inside

Kotlin
class Playlist {
    private val songs = mutableListOf<String>()
    val all: List<String> get() = songs

    fun add(song: String) {
        if (song !in songs) songs.add(song)
    }
}

fun main() {
    val p = Playlist()
    p.add("Blue Monday")
    p.add("Heroes")
    p.add("Heroes")
    println(p.all)
    println(p.all.size)
}
Output
[Blue Monday, Heroes]
2
Line-by-line explanation
  1. Line 2The mutable list is private, so only the class can change it.
  2. Line 3Others see it as a read-only List: p.all.add(...) wouldn't compile.
  3. Line 6!in checks the song isn't already there, so duplicates are ignored.

Common Kotlin Lists Mistakes

Adding to a read-only list

Don't
val names = listOf("Ana")
names.add("Ben")              // error: Unresolved reference: add
Do
val names = mutableListOf("Ana")
names.add("Ben")

Index out of bounds

Don't
val xs = listOf(1, 2, 3)
println(xs[3])                // IndexOutOfBoundsException
Do
println(xs.getOrNull(3) ?: "none")
println(xs.last())            // 3

Expecting sorted() to change the list

Don't
val nums = mutableListOf(3, 1, 2)
nums.sorted()
println(nums)                 // [3, 1, 2]
Do
nums.sort()
println(nums)                 // [1, 2, 3]

Kotlin Lists in Real Projects

Lists in apps

Every scrolling list in an Android app, such as messages, products or search results, is backed by a Kotlin list. Apps typically keep the data as an immutable list and create a new list when something changes, which makes it easy for the UI to notice the update and redraw only what changed.

Kotlin Lists FAQ

What is the difference between listOf and mutableListOf in Kotlin?

listOf gives a read-only List: you can read it, search it and create new lists from it, but there's no add or remove. mutableListOf gives a MutableList that you can change in place. Start with listOf and switch only when you really need to change the contents.

How do I create an empty list in Kotlin?

Use emptyList<String>() or listOf<String>() for a read-only empty list, and mutableListOf<String>() for one you'll add to. The type in angle brackets is needed because there are no items for Kotlin to infer it from, unless the variable already has a declared type such as val names: List<String> = emptyList().

How do I copy a list in Kotlin?

Call toList() for a read-only copy or toMutableList() for a copy you can change. The copy is a new list, so adding to or removing from it doesn't affect the original. The items themselves aren't copied, though, so if they're mutable objects, both lists still share them.

Is a Kotlin List the same as a Java ArrayList?

On the JVM, mutableListOf() creates a java.util.ArrayList underneath, and Kotlin's List and MutableList map to Java's List interface. The difference is that Kotlin splits the read-only and mutable views into two types, so the compiler stops you calling add on a list you were only meant to read.

How do I sort a list of numbers from highest to lowest?

Use sortedDescending(), which returns a new list: listOf(3, 1, 2).sortedDescending() is [3, 2, 1]. On a MutableList, sortDescending() sorts in place instead. To sort objects by a property, use sortedByDescending { it.price }.

Kotlin Lists Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the code so the list can have items added.

val cart = ("bread")
cart.add("eggs")
Predict the outputeasy

What does this print?

val xs = listOf("a", "b", "c", "d")
println(xs[2])
Predict the outputmedium

What does this print?

val xs = mutableListOf(5, 3, 8)
xs.add(0, 1)
xs.removeAt(2)
println(xs)
Multiple choicemedium

What's the difference between sort() and sorted()?

Multiple choicehard

Why can val basket = mutableListOf<String>() still have items added?

Kotlin Lists Quiz

5 questions to check what you learned.

  1. Which function creates a read-only list?

    • mutableListOf
    • arrayListOf
    • listOf
    • newList
  2. What does listOf(1, 2, 3).getOrNull(7) return?

    • 0
    • 3
    • An exception
    • null
  3. How do you check whether "milk" is in a list?

    • "milk" in list
    • list.has("milk")
    • list.find("milk") == true
    • list["milk"]
  4. What does listOf(1, 2, 3, 4).take(2) return?

    • [3, 4]
    • [1, 2]
    • 2
    • [2]
  5. When might you choose an array instead of a list?

    • For most everyday code
    • When you need to add items often
    • When printing items
    • For fixed-size, performance-critical data or when a Java API needs an array