A collection is a group of items. Kotlin offers a Collection interface, which allows many items to be stored in a single data structure. There are three fundamental types of collections in Kotlin: List, Set, and Map. The first type that you’ll learn about is List. Start a new Kotlin Playground session to follow along with the demo. Or you could use any other Kotlin programming environment of your choice.
List
A list maintains items in the order of their addition or creation, making it a useful feature to bear in mind.
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println(oceans)
}
As you can see in the console, the items are displayed in the same way they’re arranged during the list creation.
Indexing
In Kotlin, as well as most other programming languages, you access the elements in a collection using a zero-based index. This means that the first item in the list has an index of 0, not 1. This might seem counterintuitive. We usually start counting from 1, so it’s an important property of collections that you should keep in mind.
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println(oceans[0])
}
It prints “Pacific Ocean” since it’s the first item in the list. To get the second item, you increment the index by 1 until you get to the end of the list.
Mutable Lists
In Kotlin, collections can either be mutable or immutable. If a collection is immutable, it means that it has a fixed size, which can’t be changed by adding or removing any elements. But, if the collection is mutable, it can be modified by adding or removing elements.
If you want to create a mutable list, you need to use the mutableListOf function. Mutable lists are represented by the MutableList class. So, if you want to add or remove elements from a collection, you need to ensure that it’s mutable and use the appropriate functions and classes to change it. Make the previous list mutable by changing listOf to mutableListOf:
fun main() {
val oceans = mutableListOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
}
To add items to a mutable list, you can use the add method. First, remove Indian Ocean from the list, then add it back using add():
fun main() {
val oceans = mutableListOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
println(oceans)
oceans.add("Indian Ocean")
println(oceans)
}
“Indian Ocean” has been added to the list of oceans.
When working with mutable collections, you can use the removeAt() method to remove a specific item. For example, to remove the item at the third index, call removeAt(2).
fun main() {
val oceans = mutableListOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
println(oceans)
oceans.removeAt(2)
println(oceans)
}
Again, the third index is 2 and not three because indexing starts from 0 and not 1. As you can see, “Arctic Ocean” has been removed from the list.
Immutable Lists
The immutability of a list applies to the elements within the collection and not the variable or object that holds the list. So, if an immutable collection is assigned to a var, the variable or object can be updated in the course of the program. Make oceans a var, and change mutableListOf to listOf. Then reassign a new list to oceans:
fun main() {
var oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
println(oceans)
oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println(oceans)
}
The reassignment works, even though the list is immutable. If oceans is an immutable variable, attempting to perform the action mentioned earlier will result in an error. Change var to val to make the variable immutable:
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
println(oceans)
oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println(oceans)
}
As expected, reassigning any value to oceans becomes illegal since it’s now a val.
Items in the collection are immutable and can’t be updated. Unlike mutable lists, they lack an add method.
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
oceans.add("Indian Ocean") // Not possible
println(oceans)
}
You can assign a mutable list to an immutable variable and still be modified. The variable doesn’t have to be mutable because the collection is. If you make the variable immutable, you can still update its content:
fun main() {
val oceans = mutableListOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
println(oceans)
oceans.add("Indian Ocean")
println(oceans)
}
Add the Indian Ocean back to the list. Now, to know how many items are in a list, use the size property like this:
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println("There are ${oceans.size} oceans in the world.")
}
The size of the list oceans is displayed in the console after running the program.
Since a collection can contain many items, it’s possible to iterate over the entire content. Here’s how to do it in Kotlin using the for loop:
fun main() {
val oceans = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
for (ocean in oceans) {
println(ocean)
}
}
ocean, in this case, is an arbitrary variable that holds the item during each iteration. You can rename it to suit whatever works best in your situation.
Comparing Lists
To compare two lists, use the equality operator ==. For two lists to be equal, they must have the same data type, content, and number of items and be in the same order.
fun main() {
val oceans1 = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
val oceans2 = listOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
println(oceans1 == oceans2)
}
oceans1 is equal to oceans2 because all the conditions have been met. Should any condition fail, the comparison will return false. Swap the positions of Atlantic Ocean and Indian ocean in oceans2 and rerun the program - it returns false.
Sequences
Sequences and collections function differently. While collections hold data, sequences produce items as required. In Kotlin, sequences are represented by the Sequence interface. To create an empty sequence, you can use the emptySequence() function, specifying the type used in the Sequence:
fun main() {
emptySequence<String>()
}
Use sequenceOf() in-built function to create a sequence:
fun main() {
val weekdaysSequence = sequenceOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
println(weekdaysSequence.toList())
}
Sequences can be created from collections, too. In the next example, a sequence of days is created from a list of days:
fun main() {
val weekdaysList = listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
val weekdaysSequence = weekdaysList.asSequence()
println(weekdaysSequence.toList())
}
There’s a generateSequence() function that generates, well, a Sequence. generateSequence() takes a seed, which is the initial value. It also takes a function that defines how the numbers in the sequence are generated. In the example below, the function generates a sequence starting at the number 10. The generator adds 2 on every iteration, it + 2, creating a sequence of even numbers starting at 10. The code then takes the first 6 elements, prints the count and then prints the list:
fun main() {
val evens10to20 = generateSequence(10) { it + 2 }
println(evens10to20.take(6).count())
println(evens10to20.take(6).toList())
}
Sequences and collections are distinct in the way they produce items. Sequences generate elements as you iterate over them, while collections store all the items in memory.
When working with sequences, it’s important that the operation has a stopping point. This stopping point is called terminal. For example, if you don’t limit the number of iterations, your program will likely crash. This is due to the infinite nature of the sequence.
To prevent this, you can define an endpoint, such as limiting the iterations to a specific number. In the example above, the code limits the iterations to 6. It then joins the items into a string before printing it out. If you don’t define an endpoint, you’ll get an error message like the one you’d see in Kotlin Playground. Remove .take(6) from the code and rerun.
The console displays “Evaluation stopped while it’s taking too long️”.
In the next demo, you’ll learn more about the other types of collections in Kotlin. See you there.