Maps

A Map connects keys to values and looks up a value when given a key.

You create a Map by providing key-value pairs to mapOf(). Each key is separated from its associated value by to:

// Maps/Maps.kt import atomictest.eq fun main(args: Array<String>) { val constants = mapOf( "Pi" to 3.141, "e" to 2.718, "phi" to 1.618) constants eq "{Pi=3.141, e=2.718, phi=1.618}" // Look up a value from a key: constants["e"] eq 2.718 // [1] constants.keys eq setOf("Pi", "e", "phi") constants.values eq "[3.141, 2.718, 1.618]" var s = "" // Iterate through key-value pairs: for (entry in constants) // [2] s += "${entry.key}=${entry.value}, " s eq "Pi=3.141, e=2.718, phi=1.618, " s = "" // Unpack during iteration: for ((key, value) in constants) // [3] s += "$key=$value, " s eq "Pi=3.141, e=2.718, phi=1.618, " }

A plain Map is read-only. Here’s a MutableMap:

// Maps/MutableMaps.kt import atomictest.eq fun main(args: Array<String>) { val m = mutableMapOf(5 to "five", 6 to "six") m[5] eq "five" m[5] = "5ive" m[5] eq "5ive" m += 4 to "four" m eq mapOf(5 to "5ive", 4 to "four", 6 to "six") }

map[key] = value adds or changes the value associated with key. Alternatively, you can explicitly add a pair by saying map += key to value.

mapOf() and mutableMapOf() create maps that preserve the order and will iterate in the order in which the elements were put into the map. However, it’s not guaranteed for any map you’re working with.

A read-only Map doesn’t provide a way to mutate itself:

// Maps/ReadOnlyMaps.kt import atomictest.eq fun main(args: Array<String>) { val m = mapOf(5 to "five", 6 to "six") m[5] eq "five" // m[5] = "5ive" // Fails // m += (4 to "four") // Fails m + (4 to "four") // Doesn't change m m eq mapOf(5 to "five", 6 to "six") val m2 = m + (4 to "four") m2 eq mapOf( 5 to "five", 6 to "six", 4 to "four") }

The definition of m creates a Map associating Ints with Strings. If we try to replace a String, the compiler produces an error.

An expression with + just creates a new Map that includes both the old elements and the new one, but doesn’t affect the original Map. The only way to add an element is to create a new Map.

A Map returns null if it doesn’t contain an entry for a given key. If you need a result that can’t be null, use getValue() and catch NoSuchElementException if the key is missing:

// Maps/GetValue.kt import atomictest.* fun main(args: Array<String>) { val map = mapOf('a' to "attempt") map['b'] eq null capture { map.getValue('b') } eq "NoSuchElementException: " + "Key b is missing in the map." }

You can store class instances as values in a Map. Here, we create a map to retrieve a Contact using a number String:

// Maps/ContactMap.kt import atomictest.eq class Contact( val name: String, val number: String ) { override fun toString(): String { return "Contact('$name', '$number')" } } fun main(args: Array<String>) { val miffy = Contact("Miffy", "1-234-567890") val cleo = Contact("Cleo", "098-765-4321") val contacts = mapOf( miffy.number to miffy, cleo.number to cleo) contacts["1-234-567890"] eq miffy contacts["1-111-111111"] eq null }

It’s also possible to use class instances as keys in a Map, but that’s a bit trickier and we’ll discuss that later in the book.

Maps look like simple little databases. Although they are quite limited compared to a full-featured database, they are nonetheless remarkably useful (and far more efficient than a database).

Previous          Next

©2018 Mindview LLC. All Rights Reserved.