三、Map
(1) Map的Value赋值
下面代码编译会出现什么结果?
test7.go
package mainimport "fmt"type Student struct {Name string}var list map[string]Studentfunc main() {list = make(map[string]Student)student := Student{"Aceld"}list["student"] = studentlist["student"].Name = "LDB"fmt.Println(list["student"])}
结果
编译失败,./test7.go:18:23: cannot assign to struct field list["student"].Name in map
分析
map[string]Student 的value是一个Student结构值,所以当list["student"] = student,是一个值拷贝过程。而list["student"]则是一个值引用。那么值引用的特点是只读。所以对list["student"].Name = "LDB"的修改是不允许的。
方法一:
package mainimport "fmt"type Student struct {Name string}var list map[string]Studentfunc main() {list = make(map[string]Student)student := Student{"Aceld"}list["student"] = student//list["student"].Name = "LDB"/*方法1:*/tmpStudent := list["student"]tmpStudent.Name = "LDB"list["student"] = tmpStudentfmt.Println(list["student"])}
其中
/*方法1:*/tmpStudent := list["student"]tmpStudent.Name = "LDB"list["student"] = tmpStudent
是先做一次值拷贝,做出一个tmpStudent副本,然后修改该副本,然后再次发生一次值拷贝复制回去,list["student"] = tmpStudent,但是这种会在整体过程中发生2次结构体值拷贝,性能很差。
方法二:
package mainimport "fmt"type Student struct {Name string}var list map[string]*Studentfunc main() {list = make(map[string]*Student)student := Student{"Aceld"}list["student"] = &studentlist["student"].Name = "LDB"fmt.Println(list["student"])}
我们将map的类型的value由Student值,改成Student指针。
var list map[string]*Student
这样,我们实际上每次修改的都是指针所指向的Student空间,指针本身是常指针,不能修改,只读属性,但是指向的Student是可以随便修改的,而且这里并不需要值拷贝。只是一个指针的赋值。
(2) map的遍历赋值
以下代码有什么问题,说明原因
test8.go
package mainimport ("fmt")type student struct {Name stringAge int}func main() {//定义mapm := make(map[string]*student)//定义student数组stus := []student{{Name: "zhou", Age: 24},{Name: "li", Age: 23},{Name: "wang", Age: 22},}//将数组依次添加到map中for _, stu := range stus {m[stu.Name] = &stu}//打印mapfor k,v := range m {fmt.Println(k ,"=>", v.Name)}}
结果
遍历结果出现错误,输出结果为
zhou => wangli => wangwang => wang
map中的3个key均指向数组中最后一个结构体。
分析
foreach中,stu是结构体的一个拷贝副本,所以m[stu.Name]=&stu实际上一致指向同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝。

正确写法
package mainimport ("fmt")type student struct {Name stringAge int}func main() {//定义mapm := make(map[string]*student)//定义student数组stus := []student{{Name: "zhou", Age: 24},{Name: "li", Age: 23},{Name: "wang", Age: 22},}// 遍历结构体数组,依次赋值给mapfor i := 0; i < len(stus); i++ {m[stus[i].Name] = &stus[i]}//打印mapfor k,v := range m {fmt.Println(k ,"=>", v.Name)}}

运行结果
zhou => zhouli => liwang => wang
