介绍
map是key-value数据结构,又称为字段或关联数组。
映射是一个存储键值对的无序集合。
基本语法
var map变量名 map[keyType]valueType
key可以是很多类型,比如 bool, 数字, string, 指针, channel。还可以是只包含前面几个类型的接口,结构体,数组,通常为 int, string
注意: slice, map 还有 function 不可以,因为这几个没发用 == 判断
value的类型和key基本一样,通常为 数字、string ,map , struct
声明
// 只声明
var a map[string]string
fmt.Printf("a类型: %T, 值: %v \n", a, a) // a类型: map[string]string, 值: map[]
a["test"] = "xiao" // panic: assignment to entry in nil map
// 使用 make 声明
var cities01 = make(map[string]string)
fmt.Printf("cities01 类型: %T, 值: %v \n", cities01, cities01)
// cities01 类型: map[string]string, 值: map[]
// 声明并赋值
var cities map[string]string = map[string]string{
"beijing": "北京", // 必须要写 , 逗号
}
fmt.Printf("cities类型: %T, 值: %v \n", cities, cities)
// cities类型: map[string]string, 值: map[beijing:北京]
注意: 声明是不会分配内存的,初始化需要 make , 分配内存后才可以赋值和使用。否则报错
- make 作用就是分配空间
- key重复的话,value值就会覆盖调原先的,
- 不同key对应的value值可以相同
- key是无序的,每次迭代映射的时候顺序可能不一样(原因是映射的实现使用了散列表)
散列函数的目的是生成一个索引。
var a map[string]string
fmt.Printf("a类型: %T, 值: %v \n", a, a) // a类型: map[string]string, 值: map[]
// make 作用就是分配空间
a = make(map[string]string, 2) // 可以存放2个key-value
a["test"] = "xiao"
a["test"] = "xiao~"
// key重复的话,value值就会覆盖调原先的,key是无序的
fmt.Printf("a类型: %T, 值: %v \n", a, a) // a类型: map[string]string, 值: map[test:xiao~]
散列表
映射的散列表包含一组桶。在存储、删除或者查找键值对的时候,所有操作都要先选择一个桶。把操作映射时指向的键传给映射的散列函数,就能选中对应的桶。这个散列函数的目的是生成一个索引,这个索引最终将键值对分布到所有可用的桶里。
随着映射存储的增加,索引分布越均匀,访问键值对的速度越快。
映射内存
映射使用两个数据结构来存储数据。
- 第一个数据结构是一个数组,内部存储的是用于选择桶的散列键的高八位值。这个数组用于区分每个键值对要存在哪个桶里。
第二个数据结构是一个字节数组,用于存储键值对。该字节数组依次存储了这个桶里的所有的键,之后依次存储了这个桶里所有的值。实现这种键值对的存储方式目的在于减少每个桶所需的内存。
map使用细节:
map是引用类型,遵守引用类型的传递机制,在一个函数接受map后,修改后,会直接修改原map
- map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态增加键值。
- map的value经常使用struct类型,更适合管理复杂的数据。
练习
多维map
```javascript // 练习 // 存放学生信息,学生有name和sex字段 studentMap := make(map[string]map[string]string) fmt.Printf(“studentMap 类型: %T, 值: %v \n”, studentMap, studentMap) studentMap[“01”] = make(map[string]string) studentMap[“01”][“name”] = “xiao” studentMap[“01”][“sex”] = “男”
studentMap[“02”] = make(map[string]string) studentMap[“02”][“name”] = “xiao” studentMap[“02”][“sex”] = “男” // 增加一个addr字段 studentMap[“02”][“addr”] = “上海” fmt.Println(“studentMap = “, studentMap) // studentMap = map[01:map[name:xiao sex:男] 02:map[addr:上海 name:xiao sex:男]] fmt.Printf(“studentMap[01][addr] = %q”, studentMap[“01”][“addr”]) // studentMap[01][addr] = “”
<a name="ixfwT"></a>
## map 结构体(更常用)
上面存储学生信息的练习,采用结构体更加合适,且清晰简单
```javascript
// map 结构体
/*
key是学号, 唯一的
value是结构体
*/
// 自定义结构体类型 Stu
type Stu struct {
Name string
Age int
Addr string
}
students := make(map[string]Stu, 10)
stu1 := Stu{"tom", 10, "上海"}
fmt.Printf("stu1类型:%T, 值: %v \n", stu1, stu1)
// stu1类型:main.Stu, 值: {tom 10 上海}
stu2 := Stu{"jerry", 9, "上海01"}
students["001"] = stu1
students["002"] = stu2
fmt.Println("students = ", students)
// students = map[001:{tom 10 上海} 002:{jerry 9 上海01}]
// 遍历学生信息
for k, v := range students {
fmt.Printf("编号: %v\t", k)
fmt.Printf("姓名: %v\t", v.Name)
fmt.Printf("年龄: %v\t", v.Age)
fmt.Printf("地址: %v\t", v.Addr)
fmt.Println()
}
// 编号: 001 姓名: tom 年龄: 10 地址: 上海
// 编号: 002 姓名: jerry 年龄: 9 地址: 上海01