map
(映射)是存储一系列无序的key/value
对,通过key
来对value
进行操作(增、删、改、查)。
映射的key
只能为可使用==运算符的值类型(字符串、数字、布尔、数组),value
可以为任意类型
map
的设计也被称为The dictionary problem
,它的任务是设计一种数据结构用来维护一个集合的数据,并且可以同时对集合进行增删查改的操作
Go
里的map
用于存放key/value
对,在其它地方常称为hash
、dictionary
、关联数组,这几种称呼都是对同一种数据结构的不同称呼,它们都用于将key
经过hash
函数处理,然后映射到value
,实现一一对应的关系
映射是存储一系列无序的key/value
对,通过key
来对value
进行操作(增、删、改、查)
map
的key
至少可以有==、!=运算,值可以为整数、字符串、数组
value
可以是任意类型
map
声明需要指定组成元素key
和value
的类型,在声明后,会被初始化为nil
,表示暂不存在的映射0
var scores map[string]int // nil映射,光声明map类型但是没有初始化 fmt.Printf("%T %#v\n", 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\n", a, b) // a[0] = "a" // b[0] = "a" a = map[int]string{0: "a"} b = []string{"a"} fmt.Printf("%p, %p\n", a, b)
所以,map
类型实际上就是一个指针, 具体为*hmap
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, }
使用len
函数获取映射元素的数量
当访问key
存在与映射时则返回对应的值,否则返回值类型的零值
key
是否存在通过key
访问元素时可接收两个值,第一个值为value
,第二个值为bool
类型表示元
素是否存在,若存在为true
,否则为false
使用key
对映射赋值时当key
存在则修改key
对应的value
,若key
不存在则增加 key
和value
使用delete
函数删除映射中已经存在的key
可通过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]) } }
多级映射:定义映射的映射
// 名字 => 映射[字符串]字符串{"1", "2", "3"} var users map[string]map[string]string users = map[string]map[string]string{"北京": {"1": "朝阳", "2": "东城", "3": "西城"}} fmt.Printf("%T, %#v\n", 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:西城]]