In Kotlin, if you create a List or Map, it is Read-Only (similar to Immutable, but slightly different). What this means is, you have no add or put or remove function to add to or remove from the collection once it is constructed.
Note: if you still like to have a mutable collection, you would need to declare a MutableList or MutableMap. My recommendation is only to do that if you really need a temporary list that is mutable.
In Java, to create a List, it would normally as below
List<String> countries = new ArrayList();
countries.add("Australia");
countries.add("Korea");
countries.add("Malaysia");
But we can’t do the same in Kotlin anymore, as you can’t add to the List after it is created.
So to do as the above, you would use the listOf function provided by CollectionKt class of Kotlin.
val countries = listOf("Australia", "Korea", "Malaysia")
Similarly for Map, you could use mapOf function as below.
val mapCountryCapital = mapOf(
Pair("Australia", "Canberra"),
Pair("Malaysia", "Putrajaya"))
Assuming you have a list as below
class Country(val country: String, val continent: String)
val countries = listOf(Country("Korea", "Asia") ,
Country("Malaysia", "Asia"),
Country("Romania", "Europe"),
Country("Somalia", "Africa"))
And you would only want to filter and keep the Asia countries in a new list. You can’t use add or remove. So you can’t use for loop for the process.
With that you have to search to see how this could be achieved. It is as simple as below.
val asiaCountries = countries.filter{ it.continent == "Asia" }
Assuming you have a list as below
class Country(val country: String, val capital: String)
val countries = listOf(Country("Australia", "Canberra"),
Country("Malaysia", "Putrajaya"),
Country("Romania", "Bucharest"))
And now, you want to create a list of only the Capitals. Again, no point looping through the list to extract element from each item of the list, since you can’t mutate the list using add.
You have to search how that is possible. It is as simple as below.
val capitals = countries.map{ it.capital }
Assuming you have a list as below
class Country(val country: String, val cities: List<String>)
val countries = listOf(
Country("Australia", listOf("Canberra", "Melbourne")),
Country("Malaysia", listOf("Putrajaya", "Penang")))
You now want a flatten list of cities for the provided countries. A quick though as below, but is it incorrect, as this is storing the list of cities in a new list.
val capitals = countries.map{ it.cities } // Wrong answer
So how could you do it? Again, no loop through the list and manipulate it. So you have to search what good way to do it. The good news is, it is as simple as below.
val cities = countries.flatmap{ it.cities }
Assuming you have a list as below
class Country(val country: String, val capital: String)
val countries = listOf(Country("Australia", "Canberra"),
Country("Brazil", "Brasilia"),
Country("Malaysia", "Putrajaya"))
Searching through it would be difficult. You like to have a map so that could could easily find the capital by giving a country.
Again, the Map by default is Read-Only. So that can’t create a Map and put to it. Because of this challenge, you have to search how to get it done. Fortunately, it is also as simple as below
val mapCountryCaptial =
countries.associateBy({ it.country }, { it.capital })
Assuming you have a code as below
val cities = listOf("Xian", "Medan", "Putrajaya", "Canberra")
val mapCapitalCountry= mapOf(
Pair("Brasilia", "Brazil"),
Pair("Canberra", "Australia"),
Pair("Putrajaya", "Malaysia"))
You want to list of the Countries where it’s capital is stated in your city list. The first thought would be easy, just use map.
// Incorrect
val countriesOfListedCapital = cities.map { mapCapitalCountry[it] }
However, this will result is a list size of 4, with 2 null values within, since Xian and Medan will generate null from mapCapitalCountry[it].
To improve, you could do first filter away the null, and map it.
// Partially correct.
val countriesOfListedCapital
= cities.filter { mapCapitalCountry[it] != null }
.map { mapCapitalCountry[it] }
This now get a result of 2 item i.e. Australia and Malaysia. However, if you check in detail, the type for countriesOfListedCapital is List<String**?**>. It is a nullable type, which is not ideal.
Thinking further, perhaps we could force it non-nullable by using Elvis Operator, returning empty String instead of null; even though you know this will not happen, since all null has been filtered away.
// Almost correct
val countriesOfListedCapital
= cities.filter { mapCapitalCountry[it] != null }
.map { mapCapitalCountry[it] ?: "" }
This is now good. countriesOfListedCapital is nowList<String>. However, this is still not ideal, as you have to explicitly add the Elvis Operator, that you know does nothing.
Well, the good news is, there’s also a collection function that could achieve this, without any complication as above.
val countriesOfListedCapital
= cities.mapNotNull { mapCapitalCountry[it] }
Well, due to the Kotlin List and Map Read-Only collection restriction, it forces me to search the right way to do it, making the code so much concise.
The Kotlin collection has provided a bunch of very useful operations. Listed above are just very few of them. Many more to explore. Have fun check out!
If you like my post and want to get future update, do click 👏👏👏, follow me on medium, Twitter or Facebook