Kotlin: Remove last items from list and array
Video: Kotlin: Remove last items from list and array by Taught by Celeste AI - AI Coding Coach
Kotlin: Remove the Last Items from a List and Array
mutableList.removeAt(mutableList.lastIndex)mutates in place.list.dropLast(n)returns a new list. Arrays need.copyOfRangesince they're fixed-size.
A small puzzle that exposes the mutable/immutable split in Kotlin's collection API. Different operations for MutableList, List, and Array.
The Copilot prompt
fun main() {
val mutableList = mutableListOf("apple", "banana", "cherry", "date")
// Remove the last item
Copilot generates:
if (mutableList.isNotEmpty()) {
mutableList.removeAt(mutableList.size - 1) // removes "date"
}
println(mutableList) // [apple, banana, cherry]
removeAt for MutableList
mutableList.removeAt(index) mutates the receiver. Returns the removed element.
mutableList.size - 1 is the last valid index. Kotlin offers a clearer property:
mutableList.removeAt(mutableList.lastIndex)
lastIndex is size - 1 for non-empty lists. Cleaner than the manual subtraction.
For Kotlin 1.4+:
mutableList.removeLast()
removeLast() is the dedicated method for "remove the last element." Throws NoSuchElementException on empty. There's also removeLastOrNull() (returns nullable, no throw).
Removing N from the end
val n = 2
repeat(n) {
if (mutableList.isNotEmpty()) {
mutableList.removeLast()
}
}
Loop n times. Or do it in one operation:
val safe = mutableList.dropLast(n) // returns NEW list, original unchanged
dropLast(n) returns a new immutable list with the last n elements removed. Negative or zero n returns the list unchanged. Larger n than the list size returns an empty list.
Note the difference: removeLast() mutates; dropLast(n) creates a copy.
On immutable List
val immutable = listOf("apple", "banana", "cherry", "date")
val without_last = immutable.dropLast(1) // ["apple", "banana", "cherry"]
val without_last_three = immutable.dropLast(3) // ["apple"]
List (non-mutable) doesn't have removeAt or removeLast. Use dropLast(n) to produce a new list.
On Array
val array = arrayOf("apple", "banana", "cherry", "date")
// Arrays are fixed-size — can't shrink in place
val truncated = array.copyOfRange(0, array.size - 1)
println(truncated.joinToString()) // apple, banana, cherry
Array<T> in Kotlin has a fixed length; you can't add or remove. To "remove" an element, build a new array with copyOfRange, copyOf, or filter + toTypedArray:
val withoutLast = array.copyOf(array.size - 1) // [] // truncate
val withoutFirst = array.copyOfRange(1, array.size) // skip first
val filtered = array.filter { it != "date" }.toTypedArray()
For mutable resizable behaviour, MutableList is the right choice. Array is for fixed-size, primitive-friendly storage.
Removing while iterating (gotcha)
for (item in mutableList) {
if (someCondition) {
mutableList.remove(item) // ConcurrentModificationException!
}
}
Modifying a list while iterating it with for throws. Three safe patterns:
// 1. Iterator with explicit remove
val it = mutableList.iterator()
while (it.hasNext()) {
if (someCondition(it.next())) {
it.remove()
}
}
// 2. removeAll with a predicate (Kotlin 1.0)
mutableList.removeAll { someCondition(it) }
// 3. Build a new list
val filtered = mutableList.filter { !someCondition(it) }
removeAll(predicate) is the cleanest for "remove all matching."
Edge cases
- Empty list.
removeLast()throws;removeLastOrNull()returnsnull;dropLast(1)returns the empty list. dropLast(0). Returns the original list unchanged.dropLast(huge). Ifhuge > size, returns empty list. No error.removeAt(size). Out-of-bounds; throwsIndexOutOfBoundsException.
When to mutate vs when to copy
- Mutate when you have a
MutableListand you own it. Cheap, no allocation. - Copy (
dropLast,filter, etc.) when you have an immutableList, or when you want to preserve the original (e.g., for testing, undo, or thread-safety).
The functional style (copy) is more verbose but easier to reason about — pure functions, no shared mutation.
Common mistakes
Calling removeAt(size) instead of lastIndex. Off-by-one; throws.
Mutating a listOf(). listOf(...) returns List<T>, not MutableList<T>. add/remove aren't available. Use mutableListOf(...) for a mutable list.
Trying to removeLast() from an Array. Doesn't exist. Arrays are fixed-size. Use copyOf(size - 1).
Forgetting the repeat(n) { ... } is required for "remove last N" on mutable. Single removeLast() removes one. To remove n, loop or use dropLast(n) (returns new list).
Mutating during a for-loop. ConcurrentModificationException. Use removeAll(predicate) or build a new list.
What's next
Episode 18: Extension function — is this Int prime? Add a method to a built-in type that you don't own.
Recap
mutableList.removeLast() (Kotlin 1.4+) for in-place last-element removal. mutableList.removeAt(index) for any index. list.dropLast(n) for an immutable copy with the last n removed. Array<T> is fixed-size — use array.copyOf(newSize) or copyOfRange to "shrink." removeAll(predicate) is the safe way to filter-mutate without iterator surprises.
Next episode: extension function for prime check.