Kotlin Collections: Getting Started

In this tutorial, you’ll learn how to work with Kotlin Collections. You’ll transform data, filter it out, and use different types of collections in Kotlin! By Filip Babić.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Lists in Kotlin

Lists are a dynamic version of arrays. In terms of memory, dynamic means that you can allocate each element in any free memory slot. Because of this, you can have very large lists of data since you can scatter elements around in memory.

List collections can be both mutable and immutable in terms of their size and contents.

Lists are useful in programming. Since you’ll use lists in your everyday coding it’s important to understand them.

Once again, depending on your work environment, open the Lists.kt file or clear the Kotlin Playground code and add the following code:

fun main() {
  val list = listOf(2, 3, 5, 6, 7)
  println(list)
}

If you run the snippet above, you should see the numbers printed out. This is because all true collections override toString(). Additionally, you can use the square brackets syntax to access the values.

Try to do the following:

fun main() {
  val list = listOf(2, 3, 5, 6, 7)
  list[2] = 100
}

You’ll receive an error. This is because there is a distinct difference between a List and MutableList construct in Kotlin collections.

You can change the size and contents of Kotlin collections which start with the Mutable.

To be able to add, remove and insert elements at certain indices, you have to create a MutableList using mutableListOf(). Take the following snippet for example:

fun main() {
  val list = mutableListOf(2, 3, 5, 6, 7)
  list[2] = 100 // works now
  println(list[2]) // 100
  list.add(index = 3, element = 500)
  println(list[3]) // 500
  list.remove(7) 
  println(list) // [2, 3, 100, 500, 6]
  list.removeAt(0)
  println(list) // [3, 100, 500, 6]
}

Because the list is now mutable, you can change items at indices, add items at specific indices and remove items at provided indices.

But what if you don’t want to have duplicate elements? You’d use Sets to eliminate duplicates and preserve elements uniqueness.

Sets in Kotlin

A set is a collection of elements where each of the elements is unique and there are no duplicates. They’re useful when you need to filter out duplicates. For example, you might use a set when storing ids or users.

Sets are a more advanced version of lists, with internal filtering of data. Like lists, they can be both mutable and immutable.

To create a set, use setOf() or mutableSetOf(). If you run the snippet, you’ll see only three items will print, even though there are four items in the set initializer function. This is because there are two items with the same id and name:

data class Worker(
    val id: Int,
    val name: String
)

fun main() {
  val workers = setOf(
      Worker(id = 5, name = "Filip"),
      Worker(id = 3, name = "Mike"),
      Worker(id = 5, name = "Filip"),
      Worker(id = 4, name = "Filip")
  )
  // hashcode is used to remove duplicates
  println(workers)
}

Sets use an object’s hashCode() internally to filter out duplicates. If the set is mutable and you try to add another element to it, the new object will replace the old one. You can override hashCode() and manually determine how to differentiate the elements.

Sets are a bit different than lists when it comes to accessing data. You wouldn’t look up items in a set using indices since the index of an item is actually it’s hash code. If you know the hash code, then you should already have the value.

As such, you’ll use sets to store data inside and keep a clear, unique, collection of elements. You can still iterate over them. Check the following code:

data class Worker(
    val id: Int,
    val name: String
)

fun main() {
  val workers = mutableSetOf(
      Worker(id = 5, name = "Filip"),
      Worker(id = 3, name = "Mike"),
      Worker(id = 5, name = "Filip"),
      Worker(id = 4, name = "Filip")
  )
  println(workers) // [Worker(id=5, name=Filip), Worker(id=3, name=Mike), Worker(id=4, name=Filip)]
  val removedWorker = Worker(id = 5, name = "Filip")
  workers.remove(removedWorker)
  println(workers) // [Worker(id=3, name=Mike), Worker(id=4, name=Filip)]
}

Since Sets do not have indices, they’re considered an unordered collection. Sets don’t care about what order you insert things in, they only care to store unique object instances. Sets are also used at the core of Maps collections.

Maps in Kotlin

Maps store pairs of objects where each value has a distinctive key. Each pair can be of any type you want and contains two objects.

Maps are collections of key/value pairs. As such, maps are extremely useful when you’re trying to tie one value to a key like an id or a String identifier.

And like other collections, maps can be both mutable and immutable.

You can probably guess how to create a map by now, given that you initialize all the collections the same way. :]

fun main() {
  val httpHeaders = mapOf(
      "Authorization" to "your-api-key",
      "ContentType" to "application/json",
      "UserLocale" to "US")
}

By using mapOf() you can create a map. Each map has two generic type parameters: key and value. In the initializer, you use to() to create pairs of values with ease. Given that the pairs are of two strings, you’re creating a Map of types String, String.

As you can see, you’re creating an HTTP headers map for the authorization key, the content type format and the user’s locale. If you send this to a backend server, it can look at each of the keys and read the values paired with them.

And because maps use sets internally, by having key set all the keys are unique and there are no duplicates.

Like with sets and lists, maps are immutable by default. To create a mutable map, you have to use mutableMapOf():

fun main() {
  val httpHeaders = mutableMapOf(
      "Authorization" to "your-api-key",
      "ContentType" to "application/json",
      "UserLocale" to "US")
}

Now, you can change its values by their keys:

...
httpHeaders["Authorization"] = "something else"
println(httpHeaders["Authorization"]) // something else

Because the indices in a map are actually the keys, whichever type of key you use, you have to pass in the square brackets. Moreover, if you want to add values, you don’t add() them, per se. Instead, you have to call put(), like below:

...
httpHeaders.put("Hello", "World")
println(httpHeaders) // {Authorization=something else, ContentType=application/json, UserLocale=US, Hello=World}

or

...
httpHeaders["Hello"] = "World"
println(httpHeaders) // {Authorization=something else, ContentType=application/json, UserLocale=US, Hello=World}

This is the same as using the assignment operator with the appropriate key.

Iterating through maps is a bit different from the rest of Kotlin collections. Since maps have two objects for each element, you have to iterate over pairs, instead of single values. You can do this in the following way:

httpHeaders.forEach { key, value -> println("Value for key $key is $value") }

You iterate through the pairs. In each iteration, you receive both the key and the value for that key, since it’s often necessary to know and consume both the key and the value.

Nice work! Kotlin collections provide much more functionality for data consuming and transforming.