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-in make . 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)
	}
}

Concurrency 并发

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。赋值和删除函数在检测完写标志是复位之后,先将写标志位置位,才会进行之后的操作

Iteration order

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

Implementation 实现