Go 容器数据类型之映射(map)--建立事务关联的数据结构

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/AMimiDou_212/article/details/94994789

一、适用场景

业务和算法中需要使用任意类型的关联关系时,就需要使用映射。即一对多的关系;例如,工号与职工的对应。

二、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 语言原生提供的。

  1. 不需要初始化,直接声明即可。
  2. sync.Map 不能使用 map 的方式进行取值和修改等操作,而是使用 sync.Map 的Store 存储、Load 获取、Delete 删除 方法进行操作。
  3. 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 会有更好的性能。

猜你喜欢

转载自blog.csdn.net/AMimiDou_212/article/details/94994789