Several points that need to be paid attention to when using map in golang

Time:2021-4-29

1. Introduction

Map is a convenient and powerful built-in data structure in golang. It is an unordered group of elements of the same type, and the elements are indexed by a unique key of another type. The key can be of any type supported by the equality operator, such as integer, floating-point number, complex number, string, pointer, interface (as long as its dynamic type supports equality judgment), structure, and array. Slices cannot be used as mapping keys because their equality has not been defined. Like slicing, a map is a reference type. If the mapping is passed into a function and the contents of the mapping are changed, the modification is also visible to the caller. The uninitialized mapping value is nil.

Examples are as follows:

package main

import "fmt"

func main() {
    nameAge := make(map[string]int)
    Nameage ["Bob"] = 18 // increase
    Nameage ["Tom"] = 16 // increment
    Delete (nameage, "Bob") // delete
    Nameage ["Tom"] = 19 // change
    V: = nameage ["Tom"] // check
    fmt.Println("v=",v)
    v. OK: = nameage ["Tom"] // check, recommended usage
    if ok { 
      fmt.Println("v=",v,"ok=",ok)
    }  
    for k, v :=range nameAge {    		// ergodic
        fmt.Println(k, v)
    }  
}

Output results:

v= 19
v= 19 ok= true
tom 19

2. Precautions

2.1 the elements of map are not addressable

The element in a map is not a variable, but a value. Therefore, we cannot address the elements of the 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 the element of map is the value of structure type, the field value in structure cannot be modified directly. Examples are as follows:


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)
    }  
  }  
}

Compilation error:

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

The reason is that the map element can’t get address, that is to say, you can get personap [name], but you can’t modify it. There are two solutions. One is to use the strct pointer type for the value of the map, and the other is to use the temporary variable to set it back after each fetch.

(1) Change the element in the map to a 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) Use a temporary variable to override the original element.


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 reading and writing

The shared map needs to be locked during concurrent reading and writing. Let’s look at the error examples first

package main

import (
    "fmt"
    "time"
)

var m = make(map[int]int)

func main() {
    //A go program to write map 
    go func(){
        for i := 0; i < 10000; i++ {
            m[i] = i  
        }  
    }() 

    //A go program to 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

Read write lock (sync. Rwmutex) can be used to achieve mutually exclusive access.

package main

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

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

func main() {
    //A go program to write map 
    go func(){
        rwMutex.Lock()
        for i := 0; i < 10000; i++ {
            m[i] = i  
        }  
        rwMutex.Unlock()
    }() 

    //A go program to 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

The above is the details of several points that need to be paid attention to when using map in golang. For more information about golang map, please pay attention to other related articles in developer!