1、概述
map
(映射)是存储一系列无序的key/value
对,通过key
来对value
进行操作(增、删、改、查)。
映射的key
只能为可使用==运算符的值类型(字符串、数字、布尔、数组),value
可以为任意类型
map
的设计也被称为The dictionary problem
,它的任务是设计一种数据结构用来维护一个集合的数据,并且可以同时对集合进行增删查改的操作
Go
里的map
用于存放key/value
对,在其它地方常称为hash
、dictionary
、关联数组,这几种称呼都是对同一种数据结构的不同称呼,它们都用于将key
经过hash
函数处理,然后映射到value
,实现一一对应的关系
映射是存储一系列无序的key/value
对,通过key
来对value
进行操作(增、删、改、查)
2、声明
map
的key
至少可以有==、!=运算,值可以为整数、字符串、数组
value
可以是任意类型
map
声明需要指定组成元素key
和value
的类型,在声明后,会被初始化为nil
,表示暂不存在的映射0
var scores map[string]int // nil映射,光声明map类型但是没有初始化
fmt.Printf("%T %#v
", scores, scores)
fmt.Println(scores == nil) // true
关于nil map
和空map
空map
是不做任何赋值的map
a := map[int]string
nil map
,它将不会做任何初始化,不会指向任何数据结构
var a map[int]string
如何map
没初始化,直接赋值会报空指针
var a map[int]string
var b []string
fmt.Printf("%p, %p
", a, b)
// a[0] = "a"
// b[0] = "a"
a = map[int]string{0: "a"}
b = []string{"a"}
fmt.Printf("%p, %p
", a, b)
所以,map
类型实际上就是一个指针, 具体为*hmap
3、初始化
a) 使用字面量初始化并赋值map[ktype]vtype{k1:v1, k2:v2, …, kn:vn}
b) 使用字面量初始化空映射map[ktype]vtype{ }
c) 使用make
函数初始化
make(map[ktype]vtype)
,通过make
函数创建映射,指定容量
//scores = map[string]int{}
scores = map[string]int{"a": 8, "b": 9, "c":10}
fmt.Println(scores)
//scores = make(map[string]int, 8)
// 声明map的同时完成初始化
b := map[int]bool{
1: true,
2: false,
}
4、操作
4.1 获取元素的数量
使用len
函数获取映射元素的数量
4.2 访问
当访问key
存在与映射时则返回对应的值,否则返回值类型的零值
4.3 判断key
是否存在
通过key
访问元素时可接收两个值,第一个值为value
,第二个值为bool
类型表示元
素是否存在,若存在为true
,否则为false
4.4 修改和增加
使用key
对映射赋值时当key
存在则修改key
对应的value
,若key
不存在则增加 key
和value
4.5 删除
使用delete
函数删除映射中已经存在的key
4.6 遍历
可通过for-range
对映射中个元素进行遍历,range
返回两个元素分别为映射的key
和
value
上述操作示例:
// 增删改查
// key
fmt.Println(scores["a"]) // 8
fmt.Println(scores["d"]) // 0 key不存在则为0,但这样不准确
// v, ok := scores["c"]
// if ok {
if v, ok := scores["c"]; ok {
fmt.Println(v)
fmt.Println(ok) // false 判断某个键是否存在
}
scores["b"] = 10
fmt.Println(scores)
scores["d"] = 20
fmt.Println(scores)
// 删除
delete(scores, "c") // 按照key删除
fmt.Println(scores)
// 获取当前映射元素的数量
fmt.Println(len(scores))
// 遍历key、value
for k, v := range scores {
fmt.Println(k, v) // 遍历的顺序和添加的顺序是没有任何关系的
}
// 只遍历map中的key
for k := range scores {
fmt.Println(k) // 遍历的顺序和添加的顺序是没有任何关系的
}
// 只遍历map中的value
for _, v := range scores {
fmt.Println(v)
}
- 按照某个固定顺序遍历
map
默认情况下,对map
遍历后都是无序的,可以通过将map
中的key
存到切片中,然后对切片元素排序,最终实现对map
的有序遍历
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
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
}
// 正常遍历,无序
//for k, v := range scoreMap{
// fmt.Println(k, v)
//}
// 有序遍历,按照key从小到大的顺序遍历scoreMap
// 1、取出map中的所有key存入切片keys
var keys = make([]string, 0, 200) // 定义切片
for key := range scoreMap { // 把key添加到切片中
keys = append(keys, key)
}
// 2、对切片进行排序
sort.Strings(keys)
// 3、按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
4.7 多级map
多级映射:定义映射的映射
// 名字 => 映射[字符串]字符串{"1", "2", "3"}
var users map[string]map[string]string
users = map[string]map[string]string{"北京": {"1": "朝阳", "2": "东城", "3": "西城"}}
fmt.Printf("%T, %#v
", users, users)
_, ok := users["上海"]
fmt.Println(ok) // false
users["上海"] = map[string]string{"1": "浦东", "2": "徐汇", "3": "静安"}
fmt.Println(users) // map[上海:map[1:浦东 2:徐汇 3:静安] 北京:map[1:朝阳 2:东城 3:西城]]
users["上海"]["3"] = "虹桥"
fmt.Println(users) // map[上海:map[1:浦东 2:徐汇 3:虹桥] 北京:map[1:朝阳 2:东城 3:西城]]
delete(users["北京"], "1")
fmt.Println(users) // map[上海:map[1:浦东 2:徐汇 3:虹桥] 北京:map[2:东城 3:西城]]