Removing by position
Kotlin has no pop function. To take an item out of a MutableList by its position, call removeAt with an index. The item at that index disappears, and every item after it moves one step toward the front:
val threeArr = mutableListOf(1, 4, 6)
threeArr.removeAt(1)
println(threeArr) // [1, 6]
threeArr.removeAt(0)
println(threeArr) // [6]
The list is one item shorter after every call. Like add, removeAt exists only on a MutableList. A list made with listOf is read-only and has no such function.
Removing the last item
The last item of a list sits at index size - 1. Passing that index to removeAt removes the last item, which is what pop does in other languages:
val fruits = mutableListOf("mango", "kiwi", "grapefruit")
fruits.removeAt(fruits.size - 1)
println(fruits) // [mango, kiwi]
size is a property, so it is written without parentheses. The expression fruits.size - 1 is evaluated before the call, while the list still has three items, so the index is 2.
removeAt also returns the item it removed, so you can keep it:
val removed = fruits.removeAt(0)
println(removed) // mango
println(fruits) // [kiwi]
removeAt versus remove
A list also has remove(item), which looks for a value and removes its first occurrence. The two functions do different jobs:
removeAt(index)removes by position.remove(item)removes by value, and returnstrueif the value was found.
With a list of integers, the difference is easy to forget:
val nums = mutableListOf(10, 20, 30)
nums.remove(10) // removes the value 10
println(nums) // [20, 30]
nums.removeAt(1) // removes the item at index 1
println(nums) // [20]
Here remove(10) removes the value 10, not the item at index 10. For a list like [5, 6, 7], calling remove(7) removes the value 7, but removeAt(7) would throw an IndexOutOfBoundsException, because the list has no index 7.
The exercise
The starting code has two lists:
val fruits = mutableListOf("mango", "kiwi", "grapefruit")
val ages = mutableListOf(14, 32, 50, 21)
- Remove the last item of
fruits. CallremoveAtwithfruits.size - 1, the index of"grapefruit". - Remove the last two items of
ages. CallremoveAt(ages.size - 1)twice. The first call removes21. After it, the list has three items, so the same expression now evaluates to the new last index and the second call removes50.
val fruits = mutableListOf("mango", "kiwi", "grapefruit")
val ages = mutableListOf(14, 32, 50, 21)
fruits.removeAt(fruits.size - 1)
ages.removeAt(ages.size - 1)
ages.removeAt(ages.size - 1)
Afterwards, fruits is [mango, kiwi] and ages is [14, 32]. Because the index is calculated from size each time, the second call needs no change even though the list got shorter.
Removing more than one item
Calling removeAt repeatedly is fine for a couple of items, and Kotlin has bulk operations for the rest:
removeAll { it % 2 == 0 }removes every item for which the condition is true. On[1, 2, 3, 4, 5, 6]it leaves[1, 3, 5].clear()removes everything and leaves an empty list.
Be careful with indices when you remove inside a loop. Each removal shifts the later items, so an index you computed before the removal may now point at a different item, or past the end. Removing from the end, as this lesson does, avoids the problem because nothing after the removed item has to move.
Reading the list after each step
It helps to trace the exercise on paper. The list ages starts as [14, 32, 50, 21] with a size of 4. The first call removes the item at index 3, leaving [14, 32, 50] with a size of 3. The second call removes the item at index 2, leaving [14, 32]. Each call asks for the size of the list as it is at that moment, which is why one expression works for both calls.
Shortcuts and empty lists
Kotlin also has removeLast(), which removes and returns the last item without any arithmetic. Both it and removeAt(list.size - 1) fail on an empty list: the index would be -1, and removeAt(-1) throws IndexOutOfBoundsException. When the list may be empty, removeLastOrNull() returns null instead of failing:
val empty = mutableListOf<Int>()
println(empty.removeLastOrNull()) // null
Common mistakes
- Using
removeAt(fruits.size). The size is one past the last index, so this throws. - Using
removewhen you meanremoveAt. On a list of integers, the argument is treated as a value, not a position. - Hard-coding the index.
removeAt(3)works on a four-item list and breaks on a three-item one.size - 1follows the list. - Removing from an empty list. There is no item to remove, so the call throws.
Key takeaways
removeAt(index)removes the item at a position and returns it.- The last index is
size - 1, soremoveAt(list.size - 1)removes the last item. remove(item)works on values, not positions.- Only a
MutableListcan change its size.