Go语言学习13-map

0x00 定义

映射(map),Go语言中内置的一种类型,它将键值对相关联,我们可以通过键key来获取对应的值value。类似其他语言的集合。

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

键值对:一对匹配的信息

image-20220218162412073

Go语言中 map的定义语法如下:

map[KeyType]ValueType

其中,

  • KeyType:表示键的类型。
  • ValueType:表示键对应的值的类型。

map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

PS:key、value类型:bool、数字、string、指针、channel、还可以是只包含前面几个类型的接口、结构体、数组

PS:key通常为int、string类型、value通常为数字(整数、浮点数)、string、map、结构体

PS:slice、map、function不可

0x01 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) //
}

0x02 判断某个键是否存在

Go语言中有个判断map中键是否存在的特殊写法,格式如下:

value, ok := map[key]

举个例子:

func main() {
	var m1 map[string]int //这是map初始化
	//fmt.Println(m1 == nil) 返回的是一个true,说明此为空,还未初始化,在内存中没有开辟空间
	m1 = make(map[string]int, 10) //可以自动扩容,但是初始化的时候最好估算好该map的容量,避免在程序运行期间再动态扩容
	//10就是键值对的数量
	m1["beijing"] = 1
	m1["shanghai"] = 2
	m1["jiwuming"] = 35
	fmt.Println(m1)
	fmt.Println(m1["beijing"])
	value, ok := m1["nihao"] //约定成俗,用ok接收别的返回的bool值!如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
	if !ok {
		fmt.Println("查無此人!")
	} else {
		fmt.Println(value)
	}
}

0x03 map的遍历

Go语言中使用for range遍历map。

func main() {
	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
	for k := range scoreMap {
		fmt.Println(k)
	}
}

注意: 遍历map时的元素顺序与添加键值对的顺序无关。

0x03 特点

1、map集合在使用前一定要make

2、map的key-value是无序的

3、key是不可以重复的,如果遇到重复,后一个value会覆盖之前的

4、value是可以重复的

【4】三种map创建方式

方式一:var然后make函数(不推荐,了解)

var a map[int]string
	//只声明map,内存是没有分配空间的
	//必须通过make函数进行初始化才能分配空间
	a = make(map[int]string, 10) //map可以存放10个键值对
	a[2000] = "张三"
	a[2001] = "李四"

方式二:推断

b := make(map[int]string)
b[1] = "beijing"
b[2] = "shanghai"
b[3] = "guagndong"

方式三:类似json格式的

c := map[int]string{
    1 : "beijing",
    2 : "shanghai",
    3 : "guangdong",		//一定要注意逗号,千万不能忘掉
}

【5】map的增删改

增加与修改

image-20220218180427749

使用delete()函数删除键值对

使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

delete(map, key)

其中,

  • map:表示要删除键值对的map
  • key:表示要删除的键值对的键

示例代码如下:

func main(){
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	delete(scoreMap, "小明")//将小明:100从map中删除
	for k,v := range scoreMap{
		fmt.Println(k, v)
	}
}
	delete(b, 2)
	delete(b, 5)
	fmt.Println(b)

注意:清空操作

1、删除map中的所有key,没有一个专门的方法一次性删除,可以遍历一下key,逐个删除;或者手动一个一个删除

2、不过上面这个方法过于麻烦,所以我们怎么做呢,就是通过map = make(...),make一个新的,让原来的成为垃圾,被gc回收

查找

value, flag := b[4]
fmt.Println(value, flag)

按照指定顺序遍历map

package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())//当前时间的纳秒输出,并且随机化
	scoreMap := make(map[string]int)	//新建一个map
	for i := 0; i < 100; i++ {		
		key := fmt.Sprintf("Stu%02d", i)	//for循环输出99个学生名称,并赋予给key变量
		value := rand.Intn(100)				//随机生成1-100赋值给value变量
		scoreMap[key] = value				//将scoreMap的key和value一一对应起来
	}
	// fmt.Println(scoreMap)
	//将结果打印到切片中
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	//随后对切片进行排序
	sort.Strings(keys)
	//按照排序后的keys进行遍历
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}

}

map与slice结合

map和slice都要初始化

下面这段代码,可以看到,make函数制作了一个切片,这个切片是map[int]string类型的。下方这个panic错误是因为make了一个切片,没有任何的数值,所以我们需要新建一个数值,来保证不会显示索引越界错误。

func main() {
	m1 := make([]map[int]string, 0, 10)
	m1[0][100] = "beijing"
	fmt.Println(m1)
}

image-20220221142945088

当我将make函数里面的长度变成1,就会显示nil,为空,是因为map并没有初始化,也就是赋值之类的。所以需要对map进行初始化。

func main() {
	m1 := make([]map[int]string, 0, 10)
	m1[0][100] = "beijing"
	fmt.Println(m1)
}

image-20220221143530004

对map进行初始化

func main() {
	m1 := make([]map[int]string, 10, 10)
	m1[0] = make(map[int]string, 1)	//m1第一个map进行初始化
	m1[0][10] = "beijing"		//m1第一个map的值赋值,因为一共有10个map,剩余9个就默认是空值
	fmt.Println(m1)
}

image-20220221145304736

元素为map类型的切片

下面的代码演示了切片中的元素为map类型时的操作:

func main() {
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "小王子"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "沙河"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
}

值为切片类型的map

啥意思?就是平时我们新建map的时候,是make(map[int]string),这回把string替换成[]string切片类型。

m2 := make(map[string][]string, 10)
	m2["beijing"] = []string{"朝阳区", "海淀区", "东城区", "房山区", "昌平区"}
	m2["shanghai"] = []string{"上海外滩", "莆田区", "老八区"}
	fmt.Println(m2)

总结

举上面这两个例子的目的是为了提醒,map和切片在定义后,一定要初始化!

练习题

  1. 写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。

    思路总结一下:

    • 先字符串切割成切片,然后遍历这些切片到map
    • 再判断每个单词是否在map中
    • 累计一下每个单词出现的次数
  2. 观察下面代码,写出最终的打印结果。

func main() {
	type Map map[string][]int	//定义Map的类型为 map[string][]int,也就是值为切片的map类型
	m := make(Map)		//初始化一个Map,赋予变量给m
	s := []int{1, 2}	//s定义为一个具有两个值的切片
    s = append(s, 3)	//添加一个数值,即s = []int{1,2,3}
	fmt.Printf("%+v\n", s)	//打印s
	m["q1mi"] = s		//m的qlmi键对应值为s
	s = append(s[:1], s[2:]...)	//删除s[1]=2
    fmt.Printf("%+v\n", s)		//s={1,3}
    fmt.Printf("%+v\n", m["q1mi"])	//m["qlmi"]=[]int{1,2,3}
}

image-20220221150947419

posted @ 2022-02-22 21:10  谨言慎行啊  阅读(129)  评论(0编辑  收藏  举报