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
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()andlast()read the ends;getOrNull(5)returnsnullinstead of crashing when the index doesn't exist.isEmpty()andisNotEmpty()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
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 positionNote 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
| Function | Returns |
|---|---|
contains(x) or x in list | true 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:
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 originalnamesis still[Ana, Ben]afterwards.buildList { }lets you calladdinside 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
listOfmakes a read-only list;mutableListOfmakes one you can change.- Indexes start at 0; use first, last and getOrNull to read safely.
- add, remove, removeAt and
list[i] = xchange 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.
A to-do list
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}")
}[Buy milk, Call Grandma, Walk the dog, Finish homework]
1. Buy oat milk
2. Call Grandma
3. Finish homework
Homework on the list? trueLine-by-line explanation
- Line 3add with a position inserts there and shifts the rest along.
- Lines 6 to 7Remove by value, then replace the first item.
- Line 12in checks whether the list contains the item.
Numbers, sorting and slicing
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")
}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 scoreLine-by-line explanation
- Lines 3 to 4Number lists have max, min, sum and average built in.
- Line 6sortedDescending returns a new list.
- Line 9The original list is unchanged.
- Line 10getOrNull avoids a crash for a missing index.
Read-only outside, mutable inside
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)
}[Blue Monday, Heroes]
2Line-by-line explanation
- Line 2The mutable list is private, so only the class can change it.
- Line 3Others see it as a read-only List: p.all.add(...) wouldn't compile.
- Line 6!in checks the song isn't already there, so duplicates are ignored.
Common Kotlin Lists Mistakes
Adding to a read-only list
val names = listOf("Ana")
names.add("Ben") // error: Unresolved reference: addval names = mutableListOf("Ana")
names.add("Ben")Index out of bounds
val xs = listOf(1, 2, 3)
println(xs[3]) // IndexOutOfBoundsExceptionprintln(xs.getOrNull(3) ?: "none")
println(xs.last()) // 3Expecting sorted() to change the list
val nums = mutableListOf(3, 1, 2)
nums.sorted()
println(nums) // [3, 1, 2]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.
Complete the code so the list can have items added.
val cart = ("bread")
cart.add("eggs")
What does this print?
val xs = listOf("a", "b", "c", "d")
println(xs[2])
What does this print?
val xs = mutableListOf(5, 3, 8)
xs.add(0, 1)
xs.removeAt(2)
println(xs)
What's the difference between sort() and sorted()?
Why can val basket = mutableListOf<String>() still have items added?
Kotlin Lists Quiz
5 questions to check what you learned.
Which function creates a read-only list?
- mutableListOf
- arrayListOf
- listOf
- newList
What does
listOf(1, 2, 3).getOrNull(7)return?- 0
- 3
- An exception
- null
How do you check whether "milk" is in a list?
- "milk" in list
- list.has("milk")
- list.find("milk") == true
- list["milk"]
What does
listOf(1, 2, 3, 4).take(2)return?- [3, 4]
- [1, 2]
- 2
- [2]
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