三、Map

(1) Map的Value赋值

下面代码编译会出现什么结果?

test7.go

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. Name string
  5. }
  6. var list map[string]Student
  7. func main() {
  8. list = make(map[string]Student)
  9. student := Student{"Aceld"}
  10. list["student"] = student
  11. list["student"].Name = "LDB"
  12. fmt.Println(list["student"])
  13. }

结果

编译失败,./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"的修改是不允许的。

方法一:

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. Name string
  5. }
  6. var list map[string]Student
  7. func main() {
  8. list = make(map[string]Student)
  9. student := Student{"Aceld"}
  10. list["student"] = student
  11. //list["student"].Name = "LDB"
  12. /*
  13. 方法1:
  14. */
  15. tmpStudent := list["student"]
  16. tmpStudent.Name = "LDB"
  17. list["student"] = tmpStudent
  18. fmt.Println(list["student"])
  19. }

其中

  1. /*
  2. 方法1:
  3. */
  4. tmpStudent := list["student"]
  5. tmpStudent.Name = "LDB"
  6. list["student"] = tmpStudent

是先做一次值拷贝,做出一个tmpStudent副本,然后修改该副本,然后再次发生一次值拷贝复制回去,list["student"] = tmpStudent,但是这种会在整体过程中发生2次结构体值拷贝,性能很差。

方法二

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. Name string
  5. }
  6. var list map[string]*Student
  7. func main() {
  8. list = make(map[string]*Student)
  9. student := Student{"Aceld"}
  10. list["student"] = &student
  11. list["student"].Name = "LDB"
  12. fmt.Println(list["student"])
  13. }

我们将map的类型的value由Student值,改成Student指针。

  1. var list map[string]*Student

这样,我们实际上每次修改的都是指针所指向的Student空间,指针本身是常指针,不能修改,只读属性,但是指向的Student是可以随便修改的,而且这里并不需要值拷贝。只是一个指针的赋值。

(2) map的遍历赋值


以下代码有什么问题,说明原因

test8.go

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type student struct {
  6. Name string
  7. Age int
  8. }
  9. func main() {
  10. //定义map
  11. m := make(map[string]*student)
  12. //定义student数组
  13. stus := []student{
  14. {Name: "zhou", Age: 24},
  15. {Name: "li", Age: 23},
  16. {Name: "wang", Age: 22},
  17. }
  18. //将数组依次添加到map中
  19. for _, stu := range stus {
  20. m[stu.Name] = &stu
  21. }
  22. //打印map
  23. for k,v := range m {
  24. fmt.Println(k ,"=>", v.Name)
  25. }
  26. }

结果

遍历结果出现错误,输出结果为

  1. zhou => wang
  2. li => wang
  3. wang => wang

map中的3个key均指向数组中最后一个结构体。

分析

foreach中,stu是结构体的一个拷贝副本,所以m[stu.Name]=&stu实际上一致指向同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝

三、Map - 图1

正确写法

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type student struct {
  6. Name string
  7. Age int
  8. }
  9. func main() {
  10. //定义map
  11. m := make(map[string]*student)
  12. //定义student数组
  13. stus := []student{
  14. {Name: "zhou", Age: 24},
  15. {Name: "li", Age: 23},
  16. {Name: "wang", Age: 22},
  17. }
  18. // 遍历结构体数组,依次赋值给map
  19. for i := 0; i < len(stus); i++ {
  20. m[stus[i].Name] = &stus[i]
  21. }
  22. //打印map
  23. for k,v := range m {
  24. fmt.Println(k ,"=>", v.Name)
  25. }
  26. }

三、Map - 图2

运行结果

  1. zhou => zhou
  2. li => li
  3. wang => wang