Ma

Maps in Go

1 exercise

About Maps

In Go, map is a built-in data type that represent hash table. In other programming languages, you might know map as dict or associative array or a key/value store. If you're not familiar with such concept, map you can think map like a dictionary, which every word is the key and the definition is the element of the map.

Before we begin, I'd like to point you to go spec for map and go blog for map to dig further into map.

Syntactically, map looks like this:

map[KeyType]ElementType

KeyType must be any comparable type, while ElementType can be any valid type in Go, which means you can store anything from primitive variable to a slice.

It's important to remember that map in Go is unordered, if you try to loop trough a map and print the element, you might surprise yourself seeing that your elements printed in random order (give it a try if you like).

It is also important to know that each key is unique, meaning that assigning the same key twice will overwrite the value of the corresponding key.

map is reference type, which means if you pass it around, Go won't copy the whole map. Instead what Go will do is copy the pointer of the map, this makes passing map to a function or variable cheap. The value of an uninitialized map is nil.

You can define map as follows (we also called this a nil map);

  var foo map[string]int

To initialize a map, you can do:

  // With map literal
  foo := map[string]int{}

or

  // or with make function
  foo := make(map[string]int)

A nil map is different from initialized map, writing to an nil map will cause a runtime error

panic: assignment to entry in nil map

Therefore it's important to initialize a map before using it

Here are some operations that you can do with a map

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

If you try to retrieve the value for a key which does not exist in the map, it will return the zero value of the value type. This can confuse you, especially if the default value of your ElementType (for example, 0 for an int), is a valid value. To check whether a key exists in your map, you can use

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

As we've seen before, detecting whether a map is initialized or not is easy, we can simply compare them to nil but what about other maps? Well, since map isn't a comparable, we can't use == operator, but reflect package has something to rescue us, it's called DeepEqual, we can use it like

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

But wait, if map isn't a comparable why are we able to compare them with nil? Well, the spec has made an exception for this, see the comparable spec

The last one, if you're trying to write to a map from multiple goroutines, that will trigger the race detector, see this link and here. Alternatively, you can use sync.Map or atomic or mutex to work around this issue.

Edit via GitHub The link opens in a new window or tab

Learn Maps