A Go map is a lookup table. It returns a value from a key—or tests if one exists. We specify the type of keys and values in a map.
To get an element, we access it by name. To loop over the entire map's contents, we use a for
-loop—each key and value can be accessed separately.
This map has string
keys and string
values. The key type is specified in the brackets after the "map" keyword. The value type comes afterward.
string
"cat" to "black."string
"green" is returned.package main import "fmt" func main() { // Part 1: map animal names to color strings. // ... Create a map with composite literal syntax. colors := map[string]string{ "bird": "blue", "snake": "green", "cat": "black", } // Part 2: get color of snake. c := colors["snake"] fmt.Println(c) }green
For
-loopWith the "for" and "range" keywords we can iterate over a map. We must assign variables to the 2 return values returned by the range expression.
package main import "fmt" func main() { // Create a string to string map. animals := map[string]string{} animals["cat"] = "Mittens" animals["dog"] = "Spot" // Loop over the map. for key, value := range animals { fmt.Println(key, "=", value) } }cat = Mittens dog = Spot
Suppose we want to add keys in a loop, or with separate statements. We cannot use an initialization expression here—we must assign to add keys.
len()
of the map at this point is 0.len
operator (also used on arrays and slices) can be used on a map.package main import "fmt" func main() { // Step 1: create an empty map. names := map[int]string{} // Step 2: add 3 pairs to the map in separate statements. names[990] = "file.txt" names[1009] = "data.xls" names[1209] = "image.jpg" // Step 3: there are 3 pairs in the map. fmt.Println(len(names)) }3
Delete
We can add keys to a map by assigning them. But to delete a key and its value, we must use the delete built-in: the pair is entirely erased.
delete()
is the map from which we want to delete items.package main import "fmt" func main() { // Create an empty map and add 3 pairs to it. ids := map[string]int{} ids["steve"] = 10 ids["mark"] = 20 ids["adnan"] = 30 fmt.Println(len(ids)) // Delete one key from it. delete(ids, "steve") fmt.Println(len(ids)) }3 2
A key lookup in a map returns 2 values. If we do not need to access a variable elsewhere in the program, we can use the name "_" as its identifier to avoid errors.
if
-statements, the ok variable is set and then tested. If true, the inner blocks are reached.package main import "fmt" func main() { counts := map[string]int{ "bird": 100, "frog": 200, "dog": 30, } // The ok variable is set to true. if count, ok := counts["bird"]; ok { fmt.Println("COUNT FOR BIRD:", count) } // The ok variable is set to false. // ... The string does not exist in the map. if _, ok := counts["cat"]; ok { fmt.Println("NOT REACHED") } }COUNT FOR BIRD: 100
Here we get a slice of the keys from a map. We use a for-range
loop over the map and then append all the keys to a slice. This slice contains the map's keys.
for
-loop to collect values not keys.package main import "fmt" func main() { // Create map with three string keys. sizes := map[string]int{ "XL": 20, "L": 10, "M": 5, } // Loop over map and append keys to empty slice. keys := []string{} for key, _ := range sizes { keys = append(keys, key) } // This is a slice of the keys. fmt.Println(keys) }[XL L M]
Here we have a map of strings to strings. We loop over the "birds" map and collect the values from it. We place the values in a slice called "values."
package main import "fmt" func main() { // A simple map. birds := map[string]string{ "finch": "yellow", "parakeet": "blue", } // Place values in a string slice. values := []string{} for _, value := range birds { values = append(values, value) } // The values. fmt.Println(values) }[yellow blue]
Make()
can create a map with a capacity. This is the number of elements that can be placed in the map without resizing the map. A capacity optimizes the map.
package main import "fmt" func main() { // Create a map with a capacity of 200 pairs. // ... This makes adding the first 200 pairs faster. lookup := make(map[string]int, 200) // Use the new map. lookup["cat"] = 10 result := lookup["cat"] fmt.Println(result) }10
The Go runtime randomizes the loop order over a map when the range keyword is used. This means programs that rely on a certain ordering of elements will fail sooner.
package main import "fmt" func main() { // Create a map with three key-value pairs. lookup := map[int]int{ 1: 10, 2: 20, 3: 30, } // Loop ten times. for i := 0; i < 10; i++ { // Print all keys in range loop over map. // ... Ordering is randomized. for key := range lookup { fmt.Print(key) fmt.Print(" ") } fmt.Println() } }2 3 1 1 2 3 3 1 2 1 2 3 1 2 3 1 2 3 2 3 1 3 1 2 1 2 3 2 3 1
Func
argumentIn Go we place important units of logic in funcs. We can pass a map object to a func
like any other type. We must specify the key and value type in the argument of the func
.
package main import "fmt" func PrintGreen(colors map[string]int) { // Handle map argument. fmt.Println(colors["green"]) } func main() { // This map has two string keys. colors := map[string]int{ "blue": 10, "green": 20, } // Pass map to func. PrintGreen(colors) }20
Func
, early exitSuppose we want to use a map to track items we have already handled. We test a map for items that are present, and exit early if we have already handled the item.
package main import "fmt" func Test(handled map[string]bool, name string) { // If name already exists, return early. if _, ok := handled[name]; ok { fmt.Println("ALREADY HANDLED: ", name) return } // Add to map so the next time this is called, we will return early. fmt.Println("ADDED: ", name) handled[name] = true } func main() { // Create example map of string keys, bool values. handled := map[string]bool{} // Call Test func with these strings. Test(handled, "bird") Test(handled, "cat") Test(handled, "bird") }ADDED: bird ADDED: cat ALREADY HANDLED: bird
A map accelerates lookups: it uses a special hash code to guide a search. Here we compare a map search to a slice search.
string
map—only 4 keys are in the map. We locate the index of the value "bird."string
elements in a string
slice and try to find a matching element.package main import ( "fmt" "time" ) func main() { lookup := map[string]int{ "cat": 0, "dog": 1, "fish": 2, "bird": 3} values := []string{"cat", "dog", "fish", "bird"} temp := 0 t0 := time.Now() // Version 1: search map with lookup. for i := 0; i < 10000000; i++ { v, ok := lookup["bird"] if ok { temp = v } } t1 := time.Now() // Version 2: search slice with for-loop. for i := 0; i < 10000000; i++ { for x := range(values) { if values[x] == "bird" { temp = x break } } } t2 := time.Now() // Benchmark results. fmt.Println(temp) fmt.Println(t1.Sub(t0)) fmt.Println(t2.Sub(t1)) }3 119.0954ms map lookup 110.0776ms slice, for-loop
Here we test the speedup from using an accurate capacity. In this example both maps are created and populated with 1000 elements.
make()
function. It has an accurate capacity of 1000.package main import ( "fmt" "time" ) func main() { t0 := time.Now() // Version 1: use map with exact capacity. for i := 0; i < 10000; i++ { values := make(map[int]int, 1000) for x := 0; x < 1000; x++ { values[x] = x } if values[0] != 0 { fmt.Println(0) } } t1 := time.Now() // Version 2: no capacity. for i := 0; i < 10000; i++ { values := map[int]int{} for x := 0; x < 1000; x++ { values[x] = x } if values[0] != 0 { fmt.Println(0) } } t2 := time.Now() // Benchmark results. fmt.Println(t1.Sub(t0)) fmt.Println(t2.Sub(t1)) }599.5427ms (600 ms) capacity = 1000 1.4154736s (1415 ms) no capacity used
Sort
A map stores its keys in an order that makes them fast to look up. We cannot sort a map. But we can extract the keys (or values) from a map and sort them.
String
slicesOne common kind of map has string
slices as the values. In another example, we append strings to the values in a string
slice map.
A struct
can be used as the key to a map. With structs, we can create compound keys. A key can contain an int
and a string
together.
We used the map built-in to link keys to values. We tested that keys exist and we retrieved the appropriate values. A map provides a clear performance win.