[TOC]
### **map 使用注意的点,是否并发安全?**
map的类型是map\[key\],key类型的ke必须是可比较的,通常情况,会选择内建的基本类型,比如整数、字符串做key的类型。如果要使用struct作为key,要保证struct对象在逻辑上是不可变的。在Go语言中,map\[key\]函数返回结果可以是一个值,也可以是两个值。map是无序的,如果我们想要保证遍历map时元素有序,可以使用辅助的数据结构,例如orderedmap。
**第一,**一定要先初始化,否则panic
**第二,**map类型是容易发生并发访问问题的。不注意就容易发生程序运行时并发读写导致的panic。 Go语言内建的map对象不是线程安全的,并发读写的时候运行时会有检查,遇到并发问题就会导致panic。
### **map 循环是有序的还是无序的**
无序的,map 因扩张⽽重新哈希时,各键值项存储位置都可能会发生改变,顺序自然也没法保证
### **怎么处理对 map 进行并发访问?有没有其他方案**
1、直接加锁,mutex
~~~
var mu sync.Mutex
mu.Lock()
//此处操作map
mu.Unlock()
~~~
2、sync.map ,底层也是加锁了
~~~
// 并发安全的map
var m = sync.Map{}
func main() {
wg := sync.WaitGroup{}
// 对m执行20个并发的读写操作
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
m.Store(key, n) // 存储key-value
value, _ := m.Load(key) // 根据key取值
fmt.Printf("k=:%v,v:=%v\n", key, value)
wg.Done()
}(i)
}
wg.Wait()
}
~~~
3、RWMUTEX ,读写锁
### **map 的数据结构是什么?是怎么实现扩容?**
>**了解一下:**
golang 中 map 是一个 kv 对集合。底层使用 hash table,用链表来解决冲突 ,出现冲突时,不是每一个 key 都申请一个结构通过链表串起来,而是以 bmap 为最小粒度挂载,一个 bmap 可以放 8 个 kv。在哈希函数的选择上,会在程序启动时,检测 cpu 是否支持 aes,如果支持,则使用 aes hash,否则使用 memhash
```
type hmap struct {
count int // 元素的个数
B uint8 // buckets 数组的长度就是 2^B 个
overflow uint16 // 溢出桶的数量
buckets unsafe.Pointer // 2^B个桶对应的数组指针
oldbuckets unsafe.Pointer // 发生扩容时,记录扩容前的buckets数组指针
extra *mapextra //用于保存溢出桶的地址
}
```
**map 的容量大小**
map 容量最多可容纳 6.5*2^B 个元素,6.5 为装载因子阈值常量。装载因子的计算公式是:装载因子=填入表中的元素个数/散列表的长度,装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
**扩容条件1**:**装载因子 > 6.5**(源码中定义的)
即元素过多超过最大容量
正常情况下,如果没有溢出桶,那么一个桶中最多有8个元素,当平均每个桶中的数据超过了6.5个,那就意味着当前容量要不足了,发生扩容。
**扩容条件2**:**溢出桶的数量过多**
当 B < 15 时,如果overflow的bucket数量超过 2^B。
当 B >= 15 时,overflow的bucket数量超过 2^15。
### **slices能作为map类型的key吗?**
不能
**不能作为map key 的类型包括:**
* slices
* maps
* functions
### **Golang Map 如何扩容?**
双倍扩容:当前桶数组确实不够用了,**发生这种扩容时,元素会重排,可能会发生桶迁移**,扩容采取了一种称为“渐进式”的方式,原有的 key 并不会一 次性搬迁完毕,每次最多只会搬迁 2 个 bucket。
等量扩容:重新排列,极端情况下,重新排列也解决不了,map 存储就会蜕 变成链表,性能大大降低,此时哈希因子 hash0 的设置,可以降低此类极 端场景的发生。**元素会发生重排,但不会换桶**
### **Map 增删查到资料里**
- Go准备工作
- 依赖管理
- Go基础
- 1、变量和常量
- 2、基本数据类型
- 3、运算符
- 4、流程控制
- 5、数组
- 数组声明和初始化
- 遍历
- 数组是值类型
- 6、切片
- 定义
- slice其他内容
- 7、map
- 8、函数
- 函数基础
- 函数进阶
- 9、指针
- 10、结构体
- 类型别名和自定义类型
- 结构体
- 11、接口
- 12、反射
- 13、并发
- 14、网络编程
- 15、单元测试
- Go常用库/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go优化
- Go问题排查
- Go框架
- 基础知识点的思考
- 面试题
- 八股文
- 操作系统
- 整理一份资料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小点
- 树
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面试题
- 基础
- Map
- Chan
- GC
- GMP
- 并发
- 内存
- 算法
- docker