7 : Collections

    Kotlin has the two data structures you meet in heaven: lists and maps.

    Working with these collection types in Kotlin also allows its type inference to really shine compared to Java.

    Lists

    Kotlin supports lists: the most general and basic ordered data collection. Lists structure data by putting items in order.

    The easiest way to create a list is to use the listOf operator, which converts a variable number of arguments into a list. listOf determines the type of the list by examining its arguments.

    Lists know how long they are, support square bracket indexing, and can be used in the Kotlin for loop:

    However, one immediate gotcha: Kotlin lists are immutable.

    The first error message here is odd, but the second and third make more sense. Note that if you are programming in an IDE, it will warn you that the add method doesn’t exist, which will strike you as odd. Until you remember that you wanted a mutable list.

    If we want to create a mutable list we need to use mutableListOf.

    Here is another place where Kotlin bends toward immutability. listOf could have produced a mutable list and been paired with immutableListOf for producing immutable lists. But Kotlin encourages us to transform—rather than mutate—our data. We’ll see some extremely elegant ways to transform collections in the next lesson. But when you need to mutate a list, toMutableList will transform a list into an immutable list, while toList will transform a mutable list into an immutable list.

    Finally, it’s worth mentioning that many other kinds of ordered data structures in Kotlin have helper methods to turn them into lists. Here’s one example:

    Mutable Variables versus Mutable Collections

    Let’s stop here to identify an important distinction between variable mutability (val, var) and list mutability (List, MutableList).

    Comparison with Java and Python

    Lacking type inference, Java’s lists introduce so much syntax clutter that they can be hard to introduce to begginers. But, of course, Java does have multiple list implementations that work similarly to Kotlin’s built-in lists:

    A single line, but a rather fearsome blizzard of syntax, including needed to specify Integer twice, once on both sides of the expression. Sigh. Java.

    Python is much cleaner, of course, but without type safety:

    list = [1, 2, 4]

    Maps

    Kotlin also has built-in support for maps: the most basic associative data collection. Maps structure data by establishing mappings between keys that can be used to retrieve values.

    This map maps keys that are strings to values that are strings. Note how we initialize the map with key-value pairs use the "key to value" syntax.

    Happily, maps also support bracket notation to both access and modify key-value mappings. Syntatically this brings them more in line with Python’s dictionarys, except with type safety.

    Just like lists, Kotlin includes both immutable (mapOf) and immutable (mutableMapOf) maps, and operators to transform one to the other:

    We can iterate through maps in several ways. The .keys property allows us to access a list of all the maps keys:

    But, of course, there is a more Kotliniomatic way to do this:

    But you should rarely access a map using a for loop. We’re about to get to the right way to do this, so sit tight!

    Typing Collections

    The list and map builder functions listOf, mapOf, and the like do a nice job of inferring the types of your list or map. But what if you want to create an empty list (mutable, of course)? In that case, just like when we declared but didn’t initialize a variable, we need to provide Kotlin with the type that the collection will eventually contain.

    Maps work similarly, except that we need to provide both the key and value type:

    Even when Kotlin’s type inference works, you may want to specify your types to catch common mistakes. Consider this example:

    The problem here is that mutableMapOf inferred the type of the map to be <Int, Object> so that both 3 and "three" could be used as values. So when we extract a value from the map we get an Object, not an Int which we can add to 4.

    Including a type when declaring the map would have helped catch this mistake: