Go 高级数据类型--Map(映射)
一、适用场景
业务和算法中需要使用任意类型的关联关系时,就需要使用映射。即一对多的关系;例如,工号与职工的对应。
二、map的实现
map 使用**散列表(hash)**实现。
散列表可以看成一个数组(俗称”桶“),数组的每个元素是一个列表。散列表的查找复杂度为O(1),最坏的情况为O(n),n为元素的总个数。
三、map 基本操作-增删改查
map的定义: map[KeyType]ValueType
-
由以上可知:
-
- 键值(key-value)对成对出现
map 初始化:make() 、声明
//使用make()函数创建map 类型变量 m
var m =make(map[string]int)
//声明并初始化
var m2=map[string]int{
"w":8,
"a":4,
"s":6,
"d":2,
}
注意:第二种内容填充方式:键值对之间使用 逗号分隔。
map 键值对的删除–delete()
// 从 m2 删除 “w” 键值对
delete(m2,"w")
map 遍历访问“键值对”–for range
// 使用 for range 遍历访问 键值对
for k, v := range m2 {
fmt.Printf("%s : %d \n", k, v)
}
Output Result:
w : 8
a : 4
s : 6
d : 2
map 修改“键的值”
通过对键重新赋值,来进行键的值修改
fmt.Println("s Before:", m2["s"])
// 修改指定 键的值
m2["s"] = 66
fmt.Println("s After:", m2["s"])
OutPut Result :
s Before: 6
s After: 66
清空 map 中的所有 键值对
由于Go 的垃圾回收机制效率非常的高,而且Go 的并行垃圾回收效率比写一个清空函数更为快速,因此,Go 语言并没有提供清空 map 的语法糖(函数、方法),而是通过重新make一个新的map 。
map 是非并发安全的,并发安全的 sync.Map
Go 语言中的 map 在并发情况下,只读是线程安全的,同时读写线程不安全(形成竞态资源)
通过如下代码体验下竞态的发生过程:两个并发函数同时对map 进行读 写。
func unsafeMap() {
// 创建一个map
m := make(map[int]int)
ch := make(chan int)
// 声明一个goroutine 对 map 不停的写
go func() {
for { // 不停的写
m[1] = 1
}
}()
// 声明另一个goroutine 对 map 不停的读
go func() {
for { // 不停的读
_ = m[1]
}
}()
// 阻塞
<-ch
}
//OutPut Result :
//由于map 内部会对这种并发读写进行检查并提前发现,所以运行时会产生以下错误,并且提示发生的内存;
**fatal error: concurrent map read and map write**(发生了竞态问题)
避免竞态问题的方法:一般的方法是 加锁(性能会受到影响);另外,Go 1.9版本之后提供了效率较高的并发安全的 sync.Map。
sync.Map 的特性:
sync.Map 是在sync包下的特殊结构,因此,其不是Go 语言原生提供的。
- 不需要初始化,直接声明即可。
- sync.Map 不能使用 map 的方式进行取值和修改等操作,而是使用 sync.Map 的Store 存储、Load 获取、Delete 删除 方法进行操作。
- Range 配合回调函数进行遍历操作。
func SyncMap() {
// 声明 m
var m sync.Map
// 添加新元素
m.Store("AM", 12)
m.Store("AA", 12)
// m.Store("AA", 12)
m.Store("AE", 12)
m.Store("AC", 15)
m.Store("AFD", 17)
// 获取键的值
v, ok := m.Load("AE")
if ok {
fmt.Println(v)
} else {
fmt.Println("Not Found!")
}
// 根据键 删除键值对
m.Delete("AM")
// 遍历sync.Map 中的键值对
m.Range(func(k, v interface{}) bool {
fmt.Println("Value:", k, v)
return true
})
}
OutPut result:
12
Value: AA 12
Value: AE 12
Value: AC 15
Value: AFD 17
sync.Map 为了保证并发安全有一定的性能损失,因此,在非并发情况下,使用 map 相比 使用 sync.Map 会有更好的性能。