1. Overview

In Kotlin programming, dealing with lists is a common and essential task.

In this quick tutorial, we’ll explore various approaches to removing the last element in a Kotlin list.

2. Introduction to the Problem

There are two kinds of lists in Kotlin: MutableList and read-only list. For a mutable list, we can in-place remove the last element from the list.

However, if we’re given a read-only list, depending on the requirements, there can be two scenarios:

  • Raising an exception, such as UnsupportedOperationException
  • Since we cannot remove elements from a read-only list, we aim to get a new list object containing the elements from the input list without the last element.

In this tutorial, we won’t talk much about the first scenario. However, we’ll take a closer look at the second scenario and address different solutions.

For simplicity, we’ll use unit test assertions to verify whether each solution can produce the expected result.

So next, let’s start with in-place removing the last element from a mutable list.

3. Removing the Last Element From MutableList In-Place

Kotlin provides the removeLast() extension function to remove the last element from the mutable list:

public fun <T> MutableList<T>.removeLast(): T = if (isEmpty()) throw NoSuchElementException("List is empty.") else removeAt(lastIndex)

Therefore, we can just call this function to solve the problem:

val mutableList = mutableListOf("a", "b", "c", "d", "e")
mutableList.removeLast()
assertEquals(listOf("a", "b", "c", "d"), mutableList)

If we look at *removeLast()*‘simplementation, we can see when we call this function from an empty mutable list, NoSuchElementException is thrown:

assertThrows<NoSuchElementException> {
    mutableListOf<String>().removeLast()
}

As we can see, the removeLast() extension can straightforwardly remove the last element from a mutable list.

Alternatively, we can pass the last element’s index to removeAt() to achieve the same goal. To get a list’s last element’s index in Kotlin, we can read the property List.lastIndex:

val mutableList = mutableListOf("a", "b", "c", "d", "e")
mutableList.removeAt(mutableList.lastIndex)
assertEquals(listOf("a", "b", "c", "d"), mutableList)

It’s worth noting when a list is empty, lastIndex is -1. Therefore, if the given list is empty, removeAt(-1) throws IndexOutOfBoundsException with the message “Index -1 out of bounds for length 0“:

val emptyMutableList = mutableListOf<String>()
assertThrows<IndexOutOfBoundsException> {
    emptyMutableList.removeAt(emptyMutableList.lastIndex)
}.also {
    assertEquals("Index -1 out of bounds for length 0", it.message)
}

4. Obtaining a New List With the Last Element Removed

We’ve learned how to remove a mutable list’s last element. Next, let’s look at the read-only list scenario.

We aim to obtain a new list object containing the input list’s elements, excluding the last one. Therefore, we can see this task as creating a sublist of the input list.

The subList() function was born for this job:

val myList = listOf("a", "b", "c", "d", "e")
val result = myList.subList(0, myList.lastIndex)
assertEquals(listOf("a", "b", "c", "d"), result)

As the test above shows, we passed myList.lastIndex to subList() as “toIndex”. This is because subList() excludes the element at index toIndex.

The subList() function solves the problem. However, this approach won’t work with an empty list. We get IndexOutOfBoundsException if we try to get that sublist on an empty list:

val myEmptyList = emptyList<String>()
assertThrows<IndexOutOfBoundsException> {
    myEmptyList.subList(0, myEmptyList.lastIndex)
}.also {
    assertEquals("fromIndex: 0, toIndex: -1", it.message)
}

Apart from subList(), we can use Kotlin’s dropLast() extension to do the job. dropLast() accepts an integer n and returns a new list containing all elements except the last n elements. So, we can pass 1 to the function to remove the last element:

val myList = listOf("a", "b", "c", "d", "e")
val result = myList.dropLast(1)
assertEquals(listOf("a", "b", "c", "d"), result)

Further, it’s worth noting if we call this function on an empty list, it returns an empty list instead of throwing an exception:

val myEmptyList = emptyList<String>()
assertTrue { myEmptyList.dropLast(1).isEmpty() }

5. Conclusion

In this article, we’ve delved into the various methods of removing the last element from a Kotlin list. We’ve addressed scenarios involving both mutable and read-only lists. Furthermore, we explored the corner case of an empty input list.

As always, the complete source code for the examples is available over on GitHub.