特点
返回顺序未知
声明与初始化
var m1 map[string]int // 只是声明,依然是 nil
m2 := make(map[string]int) // 声明并创建,也就是说已经分配地址了
m3 := map[string]int{"golang":90, "python":90}
m4 := map[string]int{}
如果只声明而不初始化map,那么就会创建一个nil map。将不能用来存放键值对,比如上面的m1。
fmt.Println(m1) // map[]
fmt.Println(m1 == nil) // true
if m1 == nil {
// 空的 map
m1 = make(map[string]int)
}
通过key访问value,访问一个不存在的key,会返回value类型的零值,而不会报错。
因此,无法通过值来判断key是否存在,需要通过ok-idiom的方式
value, ok := map[key]
if ok == true {
// key是存在的
} else {
// key是不存在的
}
设置值
m2[k1] = 100
如果k1不存在就是新增
删除某个key
delete(m2, k1)
k1 不存在也不会报错,并不会真的删除,只是做了一个标记而已。
遍历map
for key, value := range m2 {
// 顺序不是固定的
}
实现按一定顺序遍历map
把key单独抽取出来,放在数组中,对数组进行排序,然后遍历数组即可。
map的并发问题
func testmap3() {
m := map[int]int{}
for i := 0; i < 21; i++ {
go func() {
m[1] = 1
}()
}
}
fatal error: concurrent map writes
readwritekeyfatal error: concurrent map read and map write
两种初始化方式的对比
var wg sync.WaitGroup
var Dog map[int]int
func main() {
num := 10
wg.Add(num)
for i := 0; i < num; i++ {
go func() {
defer wg.Done()
add()
}()
}
wg.Wait()
}
func add() {
if Dog == nil {
/*
方法1
Dog = map[int]int{
1: setV(),
2: setV(),
3: setV(),
4: setV(),
5: setV(),
6: setV(),
7: setV(),
8: setV(),
9: setV(),
10: setV(),
}
*/
// 方法2
Dog = make(map[int]int)
Dog[1] = setV()
Dog[2] = setV()
Dog[3] = setV()
Dog[4] = setV()
Dog[5] = setV()
Dog[6] = setV()
Dog[7] = setV()
Dog[8] = setV()
Dog[9] = setV()
Dog[10] = setV()
} else {
fmt.Println(Dog[10])
}
}
func setV() int {
return rand.Intn(10000)
}
方法1 实际上是先初始化一个临时变量,最后将其赋值给 Dog,即使出现了并发,也只会是后者覆盖前者。但是方法2 会出现问题:
Dog[10]Dog
也就说map的并发是会报错的,所以需要规避这个问题。
内置的并发安全的map数据结构
sync.Map{}
func (m *Map) Delete(key interface{})
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
func (m *Map) Range(f func(key, value interface{}) bool)
func (m *Map) Store(key, value interface{})
通过加锁
sync.Mutex
sync.RWMutex
sync.Once
map 为引用传递
在赋值和参数传递的时候一定要注意
func main() {
m := map[string]string{"name":"xxx"}
t2(m)
fmt.Println(m) // map[name:rao]
m1 := m
m["name"] = "xiao"
fmt.Println(m1) // map[name:xiao]
}
func t2(a map[string]string) {
a["name"] = "rao"
}
hash mapcopy
map的GC回收机制
map是只增不减的一种数组结构,那些被 delete 的 key 会被打上标记,但是不会被GC回收。对于大的map对象。如果不再使用了最好使用赋值 nil 的方式使其整个的可以被GC。