Reader small image

You're reading from  Kotlin Design Patterns and Best Practices - Second Edition

Product typeBook
Published inJan 2022
Reading LevelBeginner
PublisherPackt
ISBN-139781801815727
Edition2nd Edition
Languages
Right arrow
Author (1)
Alexey Soshin
Alexey Soshin
author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin

Right arrow

Reviewing Kotlin data structures

There are three important groups of data structures we should get familiar with in Kotlin: lists, sets, and maps. We'll cover each briefly, then discuss some other topics related to data structures, such as mutability and tuples.

Lists

A list represents an ordered collection of elements of the same type. To declare a list in Kotlin, we use the listOf() function:

val hobbits = listOf("Frodo", "Sam", "Pippin", "Merry")

Note that we didn't specify the type of the list. The reason is that the type inference can also be used when constructing collections in Kotlin, the same as when initializing variables.

If you want to provide the type of the list, you similarly do that for defining arguments for a function:

val hobbits: List<String> = listOf("Frodo", "Sam", "Pippin",   "Merry")

To access an element in the list at a particular index, we use square brackets:

println(hobbits[1]) 

The preceding code will output this:

> Sam

Sets

A set represents a collection of unique elements. Looking for the presence of an element in a set is much faster than looking it up in a list. But, unlike lists, sets don't provide indexes access.

Let's create a set of football World Cup champions until after 1994:

val footballChampions = setOf("France", "Germany", "Spain",   "Italy", "Brazil", "France", "Brazil", "Germany")
println(footballChampions) // [France, Germany, Spain,   Italy, Brazil]

You can see that each country exists in a set exactly once. To check whether an element is in a Set collection, you can use the in function:

println("Israel" in footballChampions)
println("Italy" in footballChampions) 

This gives us the following:

> false
> true

Note that although sets, in general, do not guarantee the order of elements, the current implementation of a setOf() function returns LinkedHashSet, which preserves insertion order – France appears first in the output, since it was the first country in the input.

Maps

A map is a collection of key-value pairs, in which keys are unique. The keyword that creates a pair of two elements is to. In fact, this is not a real keyword but a special function. We'll learn about it more in Chapter 5, Introducing Functional Programming.

In the meantime, let's create a map of some of the Batman movies and the actors that played Bruce Wayne in them:

val movieBatmans = mapOf(
    "Batman Returns" to "Michael Keaton",
    "Batman Forever" to "Val Kilmer",
    "Batman & Robin" to "George Clooney"
)
println(movieBatmans) 

This prints the following:

> {Batman Returns=Michael Keaton, 
> Batman Forever=Val Kilmer, 
> Batman & Robin=George Clooney}

To access a value by its key, we use square brackets and provide the key:

println(movieBatmans["Batman Returns"])

The preceding code will output this:

> Michael Keaton

Those data structures also support checking that an element doesn't exist:

println(" Batman Begins " !in movieBatmans)

We get the following output:

> true

Mutability

All of the data structures we have discussed so far are immutable or, more correctly, read-only.

There are no methods to add new elements to a list we create with the listOf() function, and we also cannot replace any element:

hobbits[0] = "Bilbo " // Unresolved reference!

Immutable data structures are great for writing concurrent code. But, sometimes, we still need a collection we can modify. In order to do that, we can use the mutable counterparts of the collection functions:

val editableHobbits = mutableListOf("Frodo", "Sam",   "Pippin", "Merry")
editableHobbits.add("Bilbo")

Editable collection types have functions such as add() that allow us to modify or, in other words, mutate them.

Alternative implementations for collections

If you have worked with JVM before, you may know that there are other implementations of sets and maps. For example, TreeMap stores the keys in a sorted order.

Here's how you can instantiate them in Kotlin:

import java.util.*
// Mutable map that is sorted by its keys 
val treeMap = java.util.TreeMap( 
    mapOf(
        "Practical Pig" to "bricks",
        "Fifer" to "straw",
        "Fiddler" to "sticks"
    )
)
 
println(treeMap.keys)

We will get the following output:

> [Fiddler, Fifer, Practical Pig]

Note that the names of the Three Little Pigs are ordered alphabetically.

Arrays

There is one other data structure we should cover in this section – arrays. In Java, arrays have a special syntax that uses square brackets. For example, an array of strings is declared String[], while a list of strings is declared as List<String>. An element in a Java array is accessed using square brackets, while an element in a list is accessed using the get() method.

To get the number of elements in an array in Java, we use the length() method, and to do the same with a collection, we use the size() method. This is part of Java's legacy and its attempts to resemble C++.

In Kotlin, array syntax is consistent with other types of collections. An array of strings is declared as Array<String>:

val musketeers: Array<String> = arrayOf("Athos", "Porthos",   "Aramis")

This is the first time we see angle brackets in Kotlin code. Similar to Java or TypeScript, the type between them is called type argument. It indicates that this array contains strings. We'll discuss this topic in detail in Chapter 4, Getting Familiar with Behavioral Patterns, while covering generics.

If you already have a collection and would like to convert it into an array, use the toTypedArray function:

listOf(1, 2, 3, 5).toTypedArray()

In terms of its abilities, a Kotlin array is very similar to a list. For example, to get the number of elements in a Kotlin array, we use the same size property as other collections.

When would you need to use arrays then? One example is accepting arguments in the main function. Previously, we've seen only main functions without arguments, but sometimes you want to pass them from a command line.

Here's an example of a main function that accepts arguments from a command line and prints all of them, separated by commas:

fun main(args: Array<String>) { 
    println(args.joinToString(", "))
}

Other cases include invoking Java functions that expect arrays or using varargs syntax, which we will discuss in Chapter 3, Understanding Structural Patterns.

As we are now familiar with some basic data structures, it's time to discuss how we can apply logic to them using if and when expressions.

Previous PageNext Page
You have been reading a chapter from
Kotlin Design Patterns and Best Practices - Second Edition
Published in: Jan 2022Publisher: PacktISBN-13: 9781801815727
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin