1. Overview
In this tutorial, we’ll discuss working with lists in Kotlin.
2. List in Kotlin
A list is a generic ordered collection of elements that can contain duplicate values. And, List in Kotlin is an interface that extends the Collection interface.
All the methods in this interface support read-only access to the list. Let’s look at how the list interface is declared:
public interface List<out E> : Collection<E>
Additionally, Kotlin has a MutableList interface to modify the elements of a list. The MutableList interface extends the MutableCollection interface. The methods within this interface allow us to add and remove elements from the list.
Let’s now see how the mutable list interface is declared:
public interface MutableList<E> : List<E>, MutableCollection<E>
Both the List and MutableList interfaces have methods that help us work with lists in Kotlin.
3. Create a List
We can create a read-only list in Kotlin using the listOf() method:
val countries = listOf("Germany", "India", "Japan", "Brazil", "Australia")
And, we can create a mutable list in Kotlin using the mutableListOf() method:
val cities = mutableListOf("Berlin", "Calcutta", "Seoul", "Sao Paulo", "Sydney")
4. Iterate Over a List
We can access a list’s elements by iterating over it. There are several ways to do it.
4.1. Loops
We can use various types of for loops to iterate over a list in Kotlin.
First, the for loop traverses the list by element. For each cycle, the variable country points to the next element in the list:
for (country in countries) {
country.length
// ...
}
There’s an alternative for loop that uses the size of the list to traverse through the elements:
for (i in 0 until countries.size) {
countries[i].length
// ...
}
We’ve also got some methods that can do the iteration for us.
The forEach() method, for example, takes a predicate function as a parameter and performs an action on each element in the list:
countries.forEach { it ->
it.length
// ...
}
And the forEachIndexed() method performs an action on each element along with providing a sequential index with the element. It takes as a parameter a predicate function that includes the index of an element and the element itself:
countries.forEachIndexed { i, e ->
e.length
// ...
}
4.2. List Iterators
We can use the listIterator() method for iterating through lists in both forward and backward directions.
The iterator can also be used to iterate over a list starting from the specified index using the listIterator(index: Int) method. Furthermore, the list iterator can provide information about the element indices using the methods nextIndex() *and previousIndex():*
fun iterateUsingListIterator() {
val iterator = countries.listIterator()
while (iterator.hasNext()) {
val country = iterator.next()
// ...
}
while (iterator.hasPrevious()) {
val country = iterator.previousIndex();
// ...
}
}
4.3. Mutable List Iterators
We can use the iterator() method provided by the MutableCollection interface. In addition to iterating over lists, this iterator also allows removing elements using the remove() method:
fun iterateUsingIterator() {
val iterator = cities.iterator()
iterator.next()
iterator.remove()
// ...
}
We can also use the listIterator() method provided by the MutableList interface. This iterator allows us to add, replace, and remove elements while iterating over the list:
fun iterateUsingMutableListIterator() {
val iterator = cities.listIterator(1)
iterator.next()
iterator.add("London")
iterator.next()
iterator.set("Milan")
// ...
}
5. Retrieve Elements in a List
We can use the get() method to retrieve a specified element in a list. Moreover, we can also use the array style accessing elements using the index operator [].
The index operator [] is less noisy than the get() method and is more convenient:
val element = countries[2]
val element = countries.get(3)
We can retrieve the first and last elements of the list using the first() and last() methods, respectively. Additionally, these two methods can also be used to search a collection for elements matching a given predicate.
For cases where no elements might match the predicate, we can use firstOrNull() and lastOrNull() methods to avoid throwing exceptions.
Let’s take a look into the following examples to retrieve the first and last element:
countries.first()
countries.last()
countries.first { it.length > 7 }
countries.last { it.startsWith("J") }
countries.firstOrNull { it.length > 8 }
6. Retrieve Parts of a List
We can use the subList() method to retrieve a part of the list. The parameters for the method are used to define a specified range of the list between the fromIndex (inclusive) and toIndex (exclusive).
The subList() method returns a view of the original list and will change with it. So, any structural changes in the original list make the behavior of the view undefined.
Let’s have a look at an example to create a sublist:
val subList = countries.subList(1, 4)
assertEquals(3, subList.size)
Moreover, the Collection interface provides another method to retrieve parts of the list.
We can use the slice() method to retrieve part of the list based on indices. All the indices are included. Unlike subList(), this method will create a new list with the subset of elements.
Let’s take a look at how the slice() method works:
val sliceListUsingIndices = countries.slice(1..4)
assertEquals(4, sliceListUsingIndices.size)
val sliceListUsingCollection = countries.slice(listOf(1, 4))
assertEquals(2, sliceListUsingCollection.size)
7. Count Elements in a List
We can use the count() method or the size property to find the number of elements in a list. The count() method also allows us to provide a predicate function as a parameter. After that, it returns the number of elements matching the given predicate.
Let’s see how to count the elements in a list:
val count = countries.count()
val count = countries.count { it.length > 5 }
val size = countries.size
8. Write Operations in a List
We can add elements to a mutable list using add() and addAll() methods.
Also, we can add elements to a specific position in a list by providing a position for element insertion as an additional argument:
cities.add("Barcelona")
cities.add(3, "London")
cities.addAll(listOf("Singapore", "Moscow"))
cities.addAll(2, listOf("Prague", "Amsterdam"))
We can remove elements from a mutable list using remove() and removeAll() methods. Additionally, we can also remove elements from a specified position in a list by using the removeAt() method.
Let’s look at an example to remove elements from a list:
cities.remove("Seoul")
cities.removeAt(1)
We can replace the element at the specified position in a list using the set() method. Moreover, we can also use the index operator [] to replace elements.
The fill() method can be used to replace all the elements in the list with the specified value.
Let’s try and use set(), [], and fill to modify our list:
cities.set(3, "Prague")
cities[4] = "Moscow"
cities.fill("Barcelona")
9. Sort Operations in a List
We can use sort() and sortDescending() methods to sort lists in Kotlin in ascending and descending order, respectively:
val sortCitiesAscending = cities.sort()
val sortCitiesDescending = cities.sortDescending()
These methods will use the natural order of the elements and will sort in-place. So, the collection must be a mutable list.
If we want to return a list after sorting, we can use sorted() and sortedDescending() methods:
val sortedCountriesAscending = countries.sorted()
val sortedCountriesDescending = countries.sortedDescending()
We can use sortBy() and sortByDescending() methods to sort lists based on specific properties of a given object. The collection must be a mutable list since methods will use the natural order of the elements and will sort in-place. If we want to return a list after sorting, we can use the sortedBy() and sortedByDescending() methods.
*We can use sortWith() and sortedWith() for sorting of lists using a Comparator object as an argument.*
For a more in-depth look into sort operations while working with lists in Kotlin, refer to our Guide to Sorting in Kotlin article.
10. Check Elements in a List
We can check a specified element in a list using the contains() method or the in operator. The latter is more fluent and is preferred in Kotlin.
We can check multiple elements in a list using the containsAll() method. Both the contains() and containsAll() methods return a boolean value.
Let’s test our lists with contains() and containsAll():
assertTrue(countries.contains("Germany"))
assertFalse("Spain" in countries)
assertTrue(cities.containsAll(listOf("Calcutta", "Sao Paulo", "Sydney")))
11. Conclusion
In this tutorial, we saw the various operations for working with lists in Kotlin. Both the List and MutableList interfaces provide several methods to handle the elements in the list.
As always, the code for these examples is available over on GitHub.
To learn more about Kotlin features, have a look at one of our Kotlin tutorials.