什么是 map?
map 是由一组 <key, value>
对组成的抽象数据结构,并且同一个 key 只会出现一次。
<key,value>
组成- 每个
key
只会出现一次
map 基本使用
初始化 map
// 初始化 - 不指定长度
aMap := make(map[string]string)
// 初始化 - 指定长度
bMap := make(map[string]string,16)
// 声明一个 map 为 nil,不能向其添加元素,会直接panic; 可以读取,源码会返回零值
cMap := map[string]string
// 支持在声明的时候填充元素, 此时 map 不为 nil
dMap := map[string]string{}
通过 key 获取 value
func main() {
scoreMap := make(map[string]int)
scoreMap["小红"] = 90
scoreMap["小明"] = 80
// 如果 key 存在 ok 为 true, v 为对应的值;不存在 ok 为 false,v 为值类型的零值
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v)
}
// 也可以不用 ok 模式,直接取 key
v2 := scoreMap["小明"]
// 一个不存在的 key 会返回对应类型的零值
v3 := scoreMap["xx"]
fmt.Println(v2,v3)
}
map 遍历
func main() {
scoreMap := make(map[string]int)
scoreMap["小红"] = 90
scoreMap["小明"] = 80
scoreMap["小号"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
只遍历 key
func main() {
scoreMap := make(map[string]int)
scoreMap["小红"] = 90
scoreMap["小明"] = 80
scoreMap["小号"] = 60
for k := range scoreMap {
fmt.Println(k)
}
}
遍历map时的元素顺序与添加键值对的顺序无关,更准确的说法应该是:迭代 map 的结果是无序的。关于此有两点原因:
- map 在扩容后,会发生 key 的搬迁,遍历 map 的结果就不可能按原来的顺序了。(那如果我不做删除和插入操作,按理说每次遍历这样的 map 都会返回一个固定顺序的 key/value 序列吧,然而并非如此)
- 从 go 1.0 以后,map 就变得完全无序了,当我们在遍历 map 时,并不是固定地从 0 号 bucket 开始遍历,每次都是从一个随机值序号的 bucket 开始遍历,并且是从这个 bucket 的一个随机序号的 cell 开始遍历。这样,即使你是一个写死的 map,仅仅只是遍历它,也不太可能会返回一个固定序列的 key/value 对了。
删除键值对
func main(){
scoreMap := make(map[string]int)
scoreMap["小红"] = 90
scoreMap["小明"] = 80
scoreMap["小号"] = 60
delete(scoreMap, "小明")//将小明:100从map中删除
for k,v := range scoreMap{
fmt.Println(k, v)
}
}
删除 key 并不会释放内存,只是底层对应 value 位置赋值为空,key 位置都不一定清理内存。
按顺序遍历 map
先取出 key,按某种顺序排序后,对 key 进行遍历即可。
func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
value := rand.Intn(100) //生成0~99的随机整数
scoreMap[key] = value
}
//取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//对切片进行排序
sort.Strings(keys)
//按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
map 特性
map 有很多特性,都与其底层实现息息相关,比如:
- map 是引用类型、无序键值对集合、range 迭代随机次序返回
- map 中的 key 必须是可比较的(key 值类型必须支持 ==、!=)
- map 可判断是否等于 nil,但不支持比较操作
- 访问不存在的 key 返回对应零值(map 为 nil 时,也适用)
- map[key] 不支持寻址操作(& map[key] )(map 元素是无法取址的)- 因为扩容的存在,取地址没有意义
- 删除不存在的 key,不会引发错误
- 并发不安全,同时读写 map 会 panic
以上这些特性都是根据 map 的底层实现总结出来的,具体原因看完源码就了解了,有兴趣的可以深入了解一下 map 的底层代码,推荐文章golang map 底层实现源码解析