Go语言中提供的映射关系容器为
map
,其内部使用散列表(hash)
实现。
文章目录
一、map
map是一种无序的基于key-value
的数据结构,类似于python中的字典。Go语言中的map是引用类型,必须初始化才能使用。
二、map定义
Go语言中 map
的定义语法如下:
map[KeyType]ValueType
其中,
- KeyType:表示键的类型。
- ValueType:表示键对应的值的类型。
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。
注意:定义map,key值必须是可
Hash
,用的最多的是数字和字符串
三、map基本使用
map中的数据都是成对出现的,map的基本使用示例代码如下:
func main() {
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)
}
输出:
map[小明:100 张三:90]
100
type of a:map[string]int
map也支持在声明的时候填充元素,例如
func main() {
userInfo := map[string]string{
"username": "白雪公主",
"password": "123456",
}
fmt.Println(userInfo) //map[password:123456 username:白雪公主]
}
简单示例:
//map(映射)
func main() {
// 光声明map类型,但是没有初始化
var a map[string]int
fmt.Println(a == nil)
// map的初始化
a = make(map[string]int, 8)
fmt.Println(a == nil)
// map中添加键值对
a["小王子"] = 100
a["童话"] = 200
fmt.Printf("a:%#v\n", a)
fmt.Printf("type:%T\n", a)
// 声明map同时完成初始化
b := map[int]bool{
1: true,
2: false,
}
fmt.Printf("b:%#v\n", b)
fmt.Printf("type:%T", b)
var c map[int]int
c[100] = 200 //c 这个map没有初始化不能直接操作
fmt.Println(c)
}
四、判断某个键是否存在
Go语言中有个判断map中键是否存在的特殊写法,格式如下:
value, ok := map[key]
举个栗子:
func main() {
// 判断某个键存不存在
var scoreMap = make(map[string]int)
scoreMap["李四"] = 90
scoreMap["小明"] = 100
scoreMap["李华"] = 60
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
//v, ok := scoreMap["张三"]
_, ok := scoreMap["张三"]
if ok {
fmt.Println("张三在scoreMap中")
} else {
fmt.Println("查无此人")
}
}
五、Map的遍历
Go语言中使用for range
遍历map。
func main() {
// for range遍历map
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
如果我们只想遍历key 的时候,可以按下面的写法:
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
// 只遍历Key
for k := range scoreMap {
fmt.Println(k)
}
}
注意:遍历map时的元素顺序与添加键值对的顺序无关。
六、使用delete()函数删除键值对
使用delete()
内建函数从map中删除一组键值对,delete()
函数的格式如下:
delete(map, key)
其中:
- map:表示要删除键值对的map
- key:表示要删除的键值对的键
示例代码如下:
func main() {
// 使用delete()函数删除键值对
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明") //将小明:100从map中删除
delete(scoreMap, "栗子") //如果存在就删除,不存在就不操作,不报错
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
输出结果如下:
张三 90
娜扎 60
七、按照指定顺序遍历map
func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 200)
// 添加100个键值对
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
value := rand.Intn(100) //生成0~99的随机整数
scoreMap[key] = value
}
/*
需求:按照key 从小到大的顺序去遍历scoreMap
1.先取出所有的key存放到切片中
2.对key做排序
3.按照排序后的key对scoreMap排序
*/
//1.取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//2.对切片进行排序
sort.Strings(keys) // keys目前是一个有序的切片
//3.按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
八、元素为map类型的切片
下面的代码演示了切片中的元素为map类型时的操作:
func main() {
// 元素类型为map 的切片
var mapSlice = make([]map[string]int, 3, 3) // 只完成了切片的初始化
// [nil,nil,nil]
fmt.Println(mapSlice[0] == nil)
// 还需要完成内部元素的初始化
mapSlice[0] = make(map[string]int, 3)
mapSlice[0]["沙河小王子"]= 100
fmt.Println(mapSlice)
}
九、值为切片类型的map
下面的代码演示了map中值为切片类型的操作:
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
十、Map的相等性
map 之间不能使用 ==
操作符判断,==
只能用来检查 map 是否为 nil
。
func main() {
map1 := map[string]int{
"one": 1,
"two": 2,
}
map2 := map1
if map1 ==nil{
fmt.Println("map1为空")
}else {
fmt.Println("map1不为空")
}
if map1 == map2 { // 直接报错,不能直接比较
}
}
十一、Map 是引用类型
和 [slices]类似,map 也是引用类型。当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构。因此,当改变其中一个变量,就会影响到另一变量
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("Original person salary", personSalary)
newPersonSalary := personSalary
newPersonSalary["mike"] = 18000
fmt.Println("Person salary changed", personSalary)
}
上面程序中的第 14 行,personSalary
被赋值给 newPersonSalary
。下一行 ,newPersonSalary
中 mike
的薪资变成了 18000
。personSalary
中 Mike
的薪资也会变成 18000
。程序输出:
Original person salary map[jamie:15000 mike:9000 steve:12000]
Person salary changed map[jamie:15000 mike:18000 steve:12000]
当 map 作为函数参数传递时也会发生同样的情况。函数中对 map 的任何修改,对于外部的调用都是可见的
十二、小练习
-
写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1
func main() { // 统计一个字符串中每个单词出现的次数 // "how do you do"中每个单词出现的次数 // 0. 定义一个map[string]int var s = "how do you do" var wordCount = make(map[string]int, 10) // 1. 字符串中都有哪些单词 words := strings.Split(s, " ") // 2. 遍历单词做统计 for _, word := range words { v, ok := wordCount[word] if ok { // map中有这个单词的统计记录 wordCount[word] = v + 1 } else { // map中没有这个单词的统计记录 wordCount[word] = 1 } } for k, v := range wordCount { fmt.Println(k, v) } }
-
观察下面代码,写出最终的打印结果
func main() { type Map map[string][]int m := make(Map) s := []int{1, 2} s = append(s, 3) fmt.Printf("%+v\n", s) m["Babyboy"] = s s = append(s[:1], s[2:]...) fmt.Printf("%+v\n", s) fmt.Printf("%+v\n", m["Babyboy"]) } /* [1 2 3] [1 3] [1 3 3] */