A few points to note about using map

  • 2020-11-03 22:24:01
  • OfStack

1. Introduction

map is a convenient and powerful built-in data structure in Golang, an unordered group of elements of the same type, indexed by a unique key of the other type. Its keys can be any type supported by equality operators, such as integers, floating point Numbers, complex Numbers, strings, Pointers, interfaces (as long as their dynamic types support equality judgment), structures, and arrays. Slices cannot be used as mapping keys because their equality has not been defined. Like slice 1, the mapping is also a reference type. If you pass a map into a function and change the contents of that map, the change is also visible to callers. The uninitialized mapping value is nil.

Use examples are as follows:


package main

import "fmt"

func main() {
    nameAge := make(map[string]int)
    nameAge["bob"] = 18           // increase 
    nameAge["tom"] = 16           // increase 
    delete(nameAge, "bob")         // delete 
    nameAge["tom"] = 19           // change 
    v := nameAge["tom"]           // check 
    fmt.Println("v=",v)
    v, ok := nameAge["tom"]         // Look it up. Recommended usage 
    if ok { 
      fmt.Println("v=",v,"ok=",ok)
    }  
    for k, v :=range nameAge {   		// traverse 
        fmt.Println(k, v)
    }  
}

Output results:

[

v= 19
v= 19 ok= true
tom 19

]

2. Notes

2.1 Elements of map are not addressable

The element in map is not a variable, but a value. Therefore, we cannot address elements of map.


var m = map[int]int {
	0 : 0,
	1: 1,
}

func main() {
    fmt.Println(&m[0])
}

Operation error:

[

cannot take the address of m[0]

]

Therefore, when map's elements are struct type values, field values in the struct cannot be directly modified. Consider the following example:


package main

import (
    "fmt"
)

type person struct {
  name  string
  age  byte
  isDead bool
}

func whoIsDead(personMap map[string]person) {
  for name, _ := range personMap {
    if personMap[name].age < 50 {
      personMap[name].isDead = true
    }  
  }  
}

func main() {
  p1 := person{name: "zzy", age: 100}
  p2 := person{name: "dj", age: 99} 
  p3 := person{name: "px", age: 20} 
  personMap := map[string]person{
    p1.name: p1, 
    p2.name: p2, 
    p3.name: p3, 
  }  
  whoIsDead(personMap)
  
  for _, v :=range personMap {
    if v.isDead {
      fmt.Printf("%s is dead\n", v.name)
    }  
  }  
}

Compile error:

[

cannot assign to struct field personMap[name].isDead in map

]

The reason is that the map element is not addressable, which means you can get personMap[name], but you cannot modify it. The solution is 2, 1 is value of map using the pointer type of strct, and 2 is a temporary variable, set back after each fetch.

(1) Change the element in map to the pointer to struct.


package main

import (
    "fmt"
)

type person struct {
  name  string
  age  byte
  isDead bool
}

func whoIsDead(people map[string]*person) {
  for name, _ := range people {
    if people[name].age < 50 {
      people[name].isDead = true
    }  
  }  
}

func main() {
  p1 := &person{name: "zzy", age: 100}
  p2 := &person{name: "dj", age: 99} 
  p3 := &person{name: "px", age: 20} 
  personMap := map[string]*person {
    p1.name: p1, 
    p2.name: p2, 
    p3.name: p3, 
  }  
  whoIsDead(personMap)
  
    for _, v :=range personMap {
        if v.isDead {
            fmt.Printf("%s is dead\n", v.name)
        }  
    }  
}

Output results:

[

px is dead

]

(2) Overwrite the original element with temporary variables.


package main

import (
    "fmt"
)

type person struct {
  name  string
  age  byte
  isDead bool
}

func whoIsDead(people map[string]person) {
  for name, _ := range people {
    if people[name].age < 50 {
      tmp := people[name]
      tmp.isDead = true
      people[name] = tmp 
    }  
  }  
}

func main() {
  p1 := person{name: "zzy", age: 100}
  p2 := person{name: "dj", age: 99} 
  p3 := person{name: "px", age: 20} 
  personMap := map[string]person {
    p1.name: p1, 
    p2.name: p2, 
    p3.name: p3, 
  }  
  whoIsDead(personMap)
  
    for _, v :=range personMap {
        if v.isDead {
            fmt.Printf("%s is dead\n", v.name)
        }  
    }  
}

Output results:

[

px is dead

]

2.2 map concurrent read-write problem

Shared map needs to be locked when reading and writing concurrently. First look at the error example:


package main

import (
    "fmt"
    "time"
)

var m = make(map[int]int)

func main() {
    //1 a go Process writing map 
    go func(){
        for i := 0; i < 10000; i++ {
            m[i] = i  
        }  
    }() 

    //1 a go Cheng read map 
    go func(){
        for i := 0; i < 10000; i++ { 
            fmt.Println(m[i])  
        }  
    }() 
    time.Sleep(time.Second*20)
}

Operation error:

[

fatal error: concurrent map read and map write

]

Mutual exclusive access can be achieved using a read-write lock (sync.RWMutex).


package main

import (
    "fmt"
    "time"
    "sync"
)

var m = make(map[int]int)
var rwMutex sync.RWMutex

func main() {
    //1 a go Process writing map 
    go func(){
        rwMutex.Lock()
        for i := 0; i < 10000; i++ {
            m[i] = i  
        }  
        rwMutex.Unlock()
    }() 

    //1 a go Cheng read map
    go func(){
        rwMutex.RLock()
        for i := 0; i < 10000; i++ { 
            fmt.Println(m[i])  
        }  
        rwMutex.RUnlock()
    }() 
    time.Sleep(time.Second*20)
}

Normal operation output:

[

0
1
...
9999

]

These are the details of Golang using map. For more information about golang map, please pay attention to other articles on this site.