Map 一定要初始化分配内存后再使用
Map types are reference types, like pointers or slices, and so the value of m is
nil
it doesn't point to an initialized map; attempts to write to a nil map will cause a runtime panic. To initialize a map, use the built-inmake
. The make function allocates and initializes a hash map data structure and returns a map value that points to it.
var m map[string]int
m = make(map[string]int) // or m = map[string]int{}
m["route"] = 66
i := m["route"]
j := m["root"] // j == 0
n := len(m)
delete(m, "route")
i, ok := m["route"]
Exploiting zero values
type Node struct {
Next *Node
Value interface{}
}
var first *Node
visited := make(map[*Node]bool)
for n := first; n != nil; n = n.Next {
if visited[n] {
fmt.Println("cycle detected")
break
}
visited[n] = true
fmt.Println(n.Value)
}
Another instance of helpful zero values is a map of slices. Appending to a nil slice just allocates a new slice, so it’s a one-liner to append a value to a map of slices; there’s no need to check if the key exists
type Person struct {
Name string
Likes []string
}
var people []*Person
likes := make(map[string][]*Person)
for _, p := range people {
for _, l := range p.Likes {
likes[l] = append(likes[l], p)
}
}
Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines
, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex
var counter = struct {
sync.RWMutex
m map[string]int
}{m: make(map[string]int)}
// to read from the counter, take the read lock
counter.RLock()
var n = counter.m["some_key"]
counter.RUnlock()
fmt.Println("some_key", n)
// to write the counter, take the write lock
counter.Lock()
counter.m["some_key"]++
counter.Unlock()
type RWMap struct { // 一个读写锁保护的线程安全的map
sync.RWMutex // 读写锁保护下面的map字段
m map[int]int
}
// 新建一个RWMap
func NewRWMap(n int) *RWMap {
return &RWMap{
m: make(map[int]int, n),
}
}
func (m *RWMap) Get(k int) (int, bool) { //从map中读取一个值
m.RLock()
defer m.RUnlock()
v, existed := m.m[k] // 在锁的保护下从map中读取
return v, existed
}
func (m *RWMap) Set(k int, v int) { // 设置一个键值对
m.Lock() // 锁保护
defer m.Unlock()
m.m[k] = v
}
func (m *RWMap) Delete(k int) { //删除一个键
m.Lock() // 锁保护
defer m.Unlock()
delete(m.m, k)
}
func (m *RWMap) Len() int { // map的长度
m.RLock() // 锁保护
defer m.RUnlock()
return len(m.m)
}
func (m *RWMap) Each(f func(k, v int) bool) { // 遍历map
m.RLock() //遍历期间一直持有读锁
defer m.RUnlock()
for k, v := range m.m {
if !f(k, v) {
return
}
}
}
map 不是线程安全的,在查找、赋值、遍历、删除的过程中都会检测写标志,一旦发现写标志置位(等于1),则直接 panic。赋值和删除函数在检测完写标志是复位之后,先将写标志位置位,才会进行之后的操作
If you require a stable iteration order you must maintain a separate data structure that specifies that order.
import "sort"
var m map[int]string
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}