这是我参与「第五届青训营」笔记创作活动的第十四天
map相关
从网上找到了一些关于map的题目,按照自己的话进行了一下整理
1、map 使用注意的点,是否并发安全?
map 的key类型一定是要可以比较的,通常情况下选择内建的数据结构,如果想要用结构体作为key类型,那么这个结构体对象最好是不变的。map是无序的,如果我们想要有一个有序的map,我们可以自己封装一个map,像java的LinkedHashMap,底层用一个双向链表来维护顺序。
map是本身是有并发安全问题的。
2、map 循环是有序的还是无序的?
map的循环是无序的,因为map这种数据结构本身是由hash表实现的,hash表本身就不具备有序这个特征,并且range map在循环逻辑的时候还做了随机播种,导致每一次输出都是不一样的
m2 := make(map[int]int, 0)
m2[1] = 1
m2[2] = 2
m2[3] = 3
m2[4] = 4
for _, v := range m2 {
fmt.Println(v)
}
//打印结果
//2 4
//3 1
//4 2
//1 3
3、 map 中删除一个 key,它的内存会释放么?(常问)
map的底层是有一个hash表的,这个hash表本身就占用内存。
如果这个key对应的value是一个值类型,那么内存不会释放。如果这个key对应的value是一个引用类型,那么会释放这个元素对内存空间的占用。
4、怎么处理对 map 进行并发访问?有没有其他方案? 区别是什么?
-
使用读写锁:
通过使用sync.Mutex锁或sync.RWMutex读写锁来保证同一时刻只有一个goroutine对map进行读写。例如:
import ( "sync" ) var m sync.RWMutex var data = make(map[string]int) func readData(key string) int { m.RLock() defer m.RUnlock() return data[key] } func writeData(key string, value int) { m.Lock() defer m.Unlock() data[key] = value } -
使用内置的sync.Map
sync.Map是一个并发安全的map实现,与普通map不同,它不需要加锁就可以进行读写操作,因此可以减少死锁的风险。使用方法如下:
- 创建一个
sync.Map对象:
goCopy code m := sync.Map{}- 设置键值对:
goCopy code m.Store("key", "value")- 获取键的值:
goCopy code val, ok := m.Load("key") if ok { // Key is present } else { // Key is absent }- 删除键值对:
goCopy code m.Delete("key")- 遍历键值对:
goCopy code m.Range(func(key, value interface{}) bool { // Do something with key and value return true })这是使用
sync.Map的简单示例。它是并发安全的,因此可以支持多个协程的并发访问。 - 创建一个
5、 nil map 和空 map 有何不同?
nil map是这个对象还没有在内存中分配,还不存在,因此还不能被操作;空map是这个对象已经在内存当中分配并初始化,只是还没有使用
6、map 的数据结构是什么?是怎么实现扩容?
go map的本质也是使用hash table + 链表的方式实现。
map的基本数据结构用的是hmap + 桶实现
7、slices能作为map类型的key吗?
能够被比较的类型可以作为map类型的key,slice、map、functions都不可以作为key