Working with map in Go.
Map in Go is declared as map[KeyType]ValueType
, where KeyType
can be any type that is comparable, and ValueType
can be any type, including another map.
Map is a reference type like pointers, slice hence it should be initialized by make
before using. It can also be created and initialized by map literals:
m1 := make(map[string]string)
m2 := map[int64]float64{}
m3 := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(m1)
fmt.Println(m2)
fmt.Println(m3)
Playground
Map value can be set and retrieve by its key:
m := make(map[string]string)
m["r"] = "Rob Pike"
m["k"] = "Ken Thompson"
m["g"] = "Robert Griesemer"
v := m["r"]
v1 := m["k"]
fmt.Println(v)
fmt.Println(v1)
Playground
delete
can be used to delete a key from the map:
m := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
delete(m, "a")
delete(m, "c")
fmt.Println(m)
Playground
len
can be used to calculate length of the map:
m := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println("len=", len(m))
Playground
for-range
can be used to range over the map. Note that the keys are not guarantee in the same order each time you range over the map.
m := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
for k, v := range m {
fmt.Println(k, "=", v)
}
fmt.Println("keys are not guarantee in the same order each time...")
for k, v := range m {
fmt.Println(k, "=", v)
}
Playground
If a key doesn't exist in the map, retrieving its value return zero value
of the ValueType
. A two-value assignment can be used for checking the existence of the key:
m := make(map[string]int)
m["r"] = 1
m["k"] = 0
gValue, gExistence := m["g"]
kValue, kExistence := m["k"]
fmt.Printf("k=%d, exist: %t\n", kValue, kExistence)
fmt.Printf("g=%d, exist: %t\n", gValue, gExistence)
Playground
zero value is very useful for some cases. Below is a program to count the number of time appear in a string:
s := `Go provides a built-in map type that implements a hash table`
wordCount := make(map[string]int)
words := strings.FieldsFunc(s, func(r rune) bool{
return r == '.' || r == ' '
})
for _, w := range words {
wordCount[w]++
}
fmt.Println(wordCount)
Playground
Any comparable type can be used as key of map. Below is an example of using struct as a key:
type country struct {
name string
code int
}
visits := make(map[country]int)
visits[country{"Vietnam", 84}] = 1000
visits[country{"USA", 0}] = 1
target := country{"USA", 0}
visitTimes, visited := visits[target]
fmt.Printf("Visited %5s: %5t, number of times: %d\n", target.name, visited, visitTimes)
target = country{"India", 0}
visitTimes, visited = visits[target]
fmt.Printf("Visited %5s: %5t, number of times: %d\n", target.name, visited, visitTimes)
Playground
map is not concurrency safe. A synchronization mechanism must be applied. sync.RWMutex
can be used for synchronization.
Below is a complex example where we create 100 lines of words and start a goroutine to count number of words appear in each lines:
package main
import (
"bytes"
"fmt"
"strings"
"sync"
)
type counter struct {
sync.RWMutex
count map[string]int
}
func (c *counter) Get(w string) int {
c.RLock()
defer c.RUnlock()
return c.count[w]
}
func (c *counter) GetAll() map[string]int {
c.RLock()
defer c.RUnlock()
return c.count
}
func (c *counter) Increase(w string, t int) {
c.Lock()
defer c.Unlock()
c.count[w] += t
}
func main() {
buff := bytes.Buffer{}
for i := 0; i < 100; i++ {
for j := 0; j < 100; j++ {
buff.WriteString("a b c ")
}
buff.WriteByte('.')
buff.WriteByte('\n')
}
s := buff.String()
lines := strings.FieldsFunc(s, func(r rune) bool {
return r == '\n'
})
counter := &counter{
count: make(map[string]int),
}
var wg sync.WaitGroup
for _, line := range lines {
wg.Add(1)
line := line
go func() {
defer wg.Done()
words := strings.FieldsFunc(line, func(r rune) bool {
return r == '.' || r == ' '
})
for _, w := range words {
counter.Increase(w, 1)
}
}()
}
wg.Wait()
fmt.Println(counter.GetAll())
}
Playground
Read more about map in Go official blog: https://blog.golang.org/go-maps-in-action
Questions #
Explain why you cannot take address of a map element?
p := &m[key]