开发者学堂课程【Go语言核心编程 - 基础语法、数组、切片、Map:map 使用细节和陷阱】与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/625/detail/9662
map 使用细节和陷阱
内容介绍:
一、总结map使用细节
二、map是引用类型
三、map的容量自动扩容
四、map的value使用结构体
一、总结map使用细节
接下来介绍 map 的使用细节,这里给大家总结了3点,都非常重要。
1.map 是引用类型,遵守引用类型传递的机制,在一个函数接收 map,修改后,会直接修改原来的 map【案例演示】。
2.map 的容量达到后,再想 map 增加元素,会自动扩容,并不会发生 panic,也就是说map 能动态的增长键值对(key-value)【索例演示】。
3.map 的 value 也经常使用 struct 类型,更适合管理复杂的数据(比前面value是一个map更好),比如 value 为 Student 结构体【案例演示,因为还没有学结构体,体验一下即可】。
二、map是引用类型
来看一下 map 使用细节有哪些地方。第一个,首先要清楚的知道 map 是一个引用类型的数据,因此它就会遵守信用类型传递的机制,也就是说如果用一个函数接收map,修改过后,会直接修改原来的 map。也就是说 map 是引用类型的,这点要注意,跟切片一样,切片是引用类型,map也是引用类型。
举个例子说明特点:新建一个文件夹叫 mapdetails,然后新建一个文件名叫main.go。在之前的文件里复制一个通用的代码过来,代码如下:
package main
import (
“fmt”
)
func main() {
}
先讲map细节的第一点,测试这个特点是否正确。写一个函数 modify,在这里接收一个 map,类型是 int 类型,代码如下:
f
unc modify(map1 map[int]int) {
map1[10] = 900
//下标为10,改成了900
//但是要确保有一个key为10
}
在主函数里声明一个 map,代码如下:
map
1 := make(map[int]int)
//
make 一个 map[int]int 类型的 map
map
1
[
1] = 90
//第一个放的key为1,map等于90
m
ap1[2] = 88
//key为2,map等于88
m
ap1[10] = 1
//key为10,map等于1
m
ap1[20] = 2
//key为20,map等于2
m
odify(map1)
//调用modify的时候把map传给它
f
mt
.
Println(map1)
//看看结果,map[10] = 900,说明map是引用类型
输出观察 key 为10的这个元素对应的值是1还是900。如果输出的是900就说明它的确是引用传递类型;如果仍然保存是1,那就说明它是默认只拷贝、只传递了。
运行这段阶段代码,在 cmd.exe里输入以下命令回车:
cd
..
c
d mapdetails
go
run main.go
输出结果是:
map[
1:90 2:88 10:900 20:2]
//输出的结果和分析的结果是一样的
可以看到key为10的变成了900,也就是说当把 map 传递给 modify 函数过后,modify 里的修改会直接影响到 map 里面的 map1,从而说明 map 是引用类型。
三、map的容量自动扩容
再来看 map 使用的第二个细节。map 容量达到后,再想 map 增加元素,会自动扩容,并不会发生panic。但是切片不可以,切片得用 append 才可以。也就是说map 能动态的增长键值对(key-value)。
案例演示:
map
1 := make(map[int]int, 2)
//make一个map[int]int类型的map,默认容量是2
map
1
[
1] = 90
m
ap1[2] = 88
m
ap1[10] = 1
m
ap1[20] = 2
//key为20,map等于2
m
odify(map1)
//调用 modify 的时候把 map 传给它
f
mt
.
Println(map1)
可以看到在在map1[10]的时候已经超过了2了,但是没有报错,此时是可以运行的,运行结果是:
map
[10:900 20:2 1:90 2:88]
这说明map确实可以自动扩容。
四、map的value使用结构体
再看第三个细节,map的value也经常使用struct类型(struct就是结构体),更适合管理复杂的数据(比前面是一个map更好),比如value为Student结构体【因为还没有学结构体,体验一下即可】。(其实 map 这个值用结构体用的更多,所以这里只是写一段代码体验一下)
案例演示:
对前面的案例进行修改,map 的 key 还是用的学号,然后它的值用结构体来完成。
这里将要求粘贴过来:
// map 的 value 也经常使 struct 类型,
//更适合管理复杂的数据(比前面是一个map更好),
//比如 value 为 Student 结构体【因为还没有学结构体,体验一下即可】
//(1)map 的 key 为学生的学号,是唯一的
//(2)map 的 value 为结构体,包含学生的名字,年龄,地址
第一步要定义一个结构体:
//定义一个学生结构体
type Stu struct {
Name string
//定义名字
Age int
//定义年龄
Address string
//定义地址
}
//这就是一个学生结构体
声明一个map:
students
:= make
(
map[string]Stu, 10)
//放10个学生进去,make 一个这样的 map,map 的 key 是 string,map 的值是 Stu 结构体。
//创建2个学生
stu
1 := Stu{”tom”, 18,”
北京
”}
//这里做了简单化处理,第一个学生名字是tom,年龄是18,地址是北京
stu
2 := Stu{“mary”, 28,”
上海
”}
第一个学生名字是 mary,年龄是28,地址是上海
//现在要将两个学生交到 string 这个值中
students
[“no1”] = stu1
//学号它的 key 是 number1,是 stu1这个学生
students
[“no2”] = stu2
//学号它的 key 是 number2,是 stu2这个学生
f
mt.Println(students)
//输出这个 students,观察此时此刻是否已经用这个 map 把这个学生(string)保留起来,也就是说这个值Stu已经是个结构体了,这样肯定是更好保存一个学生的信息。
在 cmd.exe 里输入命令 go run main.go 回车得到:
map[1:90 2:88 10=900 20:2]
map [no1:<tom 18北京> no2:<mary 28
上海>]
可以很清晰的看出 map 里面有一个 no1,key 为no1,它是一个结构体:tom,18,北京;第二个学生是 no2,它的结构体是 mary,28,上海。这样就会更好一点,因为将来结构体的数据可以很轻松的增加它的储存部分,比如再增加一个属性就很轻松了。
再讲如何遍历一个学生,想把每一个学生具体的信息做出来:
//遍历各个学生信息
for
k, v := range students {
//for 第一个把key遍历出来,把值遍历出来,k就是学生的编号,v就是一个结构体fmt.Printf(“
学生的编号是%v
\
n”, k)
//每输出一个就换行
fmt.Printf(“
学生的名字是%v
\n”, v.Name)
fmt.Printf(“
学生的年龄是%v \n”, v.Age)
fmt.Printf(“
学生的地址是%v \n”, v.Address)
fmt
.Println()
//最后再换一行
}
执行看遍历是否成功,在 cmd.exe 里输入命令 go run main.go 回车得到:
map[2:88 10:900 20:2 1:90]
map[no1:<tom 18
北京>
no2:<mary 28
上海
>
]
学生的编号是no1
学生的名字是tom
学生的年龄是18
学生的地址是北京
学生的编号是no2
学生的名字是mary
学生的年龄是28
学生的地址是上海
可以看出信息是非常清晰的,第一个学生编号是 no1,名字、年龄、地址等都有;第二个学生编号是 no2,名字、年龄等如上。
这里就讲了 map 使用的第三个细节,在实际开发中 value 更多的情况下用的是结构体类型,而且这种方式来保存或者管理数据也更加的方便,更加的灵活。
关于 map 使用细节就讲完了,尤其要注意第三点,还有就是 map 是引用类型,map 的容量会自动增加。