如何精通Go Map的高级使用技巧,实现高效编程?

2026-04-11 03:002阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1582个文字,预计阅读时间需要7分钟。

如何精通Go Map的高级使用技巧,实现高效编程?

原文链接:https://www.zhoubotong.site/post/60.

之前写过一篇关于GoMap定义的几种方式及修改技巧的文章,今天发现还可以深入探讨一下开发中容易忽视的遗漏问题,以下以map为例,演示大家容易忽视的细节。

原文链接:www.zhoubotong.site/post/60.html
之前写过一篇文章,Gomap定义的几种方式以及修改技巧,今天发现还可以深入探讨下开发中容易被忽视遗漏的问题,

以下以map为例,演示大家日常开发中可能存在的问题。

Map的Value的赋值

我们来看下下面的代码编译会出现什么结果?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]Person) // 定义一个map ,值为上面Person的结构体 person := Person{"乔峰", 0} u["p"] = person u["p"].Sex = 18 // 按道理上面u["p"] 已经赋值成了person即Person{"乔峰", 0}的结构体,使用结构体.key=value应该没毛病 fmt.Println(u["p"]) }

输出:

./main.go:17:2: cannot assign to struct field u["p"].Sex in map

分析

为啥不能使用u["p"].属性赋值?map[string]Person的value是一个Person结构值,所以当使用u["p"]= person,其实是一个值拷贝过程。

而u["p"]则是一个值引用(注意:所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量是直接指向存在内存中的值。

Golang中引用类型如指针,slice,map,channel,接口,函数等),再说简单点就是:

[值类型]

包括:int、float、bool、string、数组、结构体

值类型变量声明后,不管是否已经赋值,编译器就会为它分配内存,此时该值储存在栈上,比如j=i 时候修改某变量i/j的值不会影响另一个。

[引用类型]

如何精通Go Map的高级使用技巧,实现高效编程?

包括:指针、slice切片、map、chan、interface

变量直接存放的就是一个内存地址值,这个地址值指向的空间存的才是值。所以修改器中一个,另外一个也会修改。

但是需要注意的是引用类型必须申请内存才可以使用,make()是给引用类型申请内存空间。

既然u["p"]是值引用,那么对u["p"].Sex =18的修改当然是不允许滴了。

那要怎么解决赋值问题?这里我整理了两种思路来解决:
解决方案一

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]Person) // 定义一个map ,值为上面Person的结构体 person := Person{"乔峰", 0} u["p"] = person //u["p"].Sex = 18 // 按道理上面u["p"] 已经赋值成了person即Person{"乔峰", 0}的结构体 //解决思路一 tmp := u["p"] //使用临时变量将u["p"]即person 赋值给tmp,该操作为值拷贝 tmp.Sex = 18 //tmp 具备struct u["p"] = tmp //或者person =tmp 将tmp 赋值给u["p"]即person ,该操作为值拷贝复制回去覆盖 fmt.Println(u["p"]) }

以上输出:

{乔峰 18}

上面tmp:=u["p"]是先做一次值拷贝,做出一个tmp副本,然后修改该副本,然后再次发生了一次值拷贝复制回去:u["p"]=tmp,但是这种会在整个过程中发生2次结构体值拷贝,很明显这周搞法性能很差。

自己觉的都看不下去了,有什么更好的实现方案?接下来我们看下思路二:

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) // 注意这里的变化:定义一个map ,值为上面Person的结构体指针 person := Person{"乔峰", 0} u["p"] = &person u["p"].Sex = 18 fmt.Println(u["p"]) }

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

这样我们实际上每次修改的都是指针所指向的Person空间,指针本身是常指针,不能修改,只读属性,但是指向的Person是可以随便修改的,

而且这里我们看到并不需要值拷贝。只是一个指针的赋值。代码明显精简了很多。

Map的遍历赋值

我们来看下下面的代码有什么问题?能说明原因吗?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) // 注意这里的变化:定义一个map ,值为上面Person的结构体指针 //定义Person数组 persons := []Person{ {Name: "乔峰", Sex: 18}, {Name: "鸠摩智", Sex: 23}, {Name: "慕容复", Sex: 22}, } //将数组依次添加到map中 for _, person := range persons { u[person.Name] = &person } //打印map for k, v := range u { fmt.Println(k, "=>", v.Name) } }

遍历结果出现非预期的结果。仔细看,结果是错误的:

乔峰 => 慕容复 鸠摩智 => 慕容复 慕容复 => 慕容复

我们发现map中的3个key都指向数组中最后一个结构体。

分析

for循环中,person是结构体的一个拷贝副本,所以u

[person.Name]=&person

实际上一直都是指向了同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝

怎么改?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) //定义Person数组 persons := []Person{ {Name: "乔峰", Sex: 18}, {Name: "鸠摩智", Sex: 23}, {Name: "慕容复", Sex: 22}, } // 遍历结构体数组,依次赋值给map for i := 0; i < len(persons); i++ { u[persons[i].Name] = &persons[i] } //打印map for k, v := range u { fmt.Println(k, "=>", v.Name) } }

输出:

鸠摩智 => 鸠摩智 慕容复 => 慕容复 乔峰 => 乔峰

通过上面的示例,希望能让大家少走弯路,避免踩坑。

无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。

本文共计1582个文字,预计阅读时间需要7分钟。

如何精通Go Map的高级使用技巧,实现高效编程?

原文链接:https://www.zhoubotong.site/post/60.

之前写过一篇关于GoMap定义的几种方式及修改技巧的文章,今天发现还可以深入探讨一下开发中容易忽视的遗漏问题,以下以map为例,演示大家容易忽视的细节。

原文链接:www.zhoubotong.site/post/60.html
之前写过一篇文章,Gomap定义的几种方式以及修改技巧,今天发现还可以深入探讨下开发中容易被忽视遗漏的问题,

以下以map为例,演示大家日常开发中可能存在的问题。

Map的Value的赋值

我们来看下下面的代码编译会出现什么结果?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]Person) // 定义一个map ,值为上面Person的结构体 person := Person{"乔峰", 0} u["p"] = person u["p"].Sex = 18 // 按道理上面u["p"] 已经赋值成了person即Person{"乔峰", 0}的结构体,使用结构体.key=value应该没毛病 fmt.Println(u["p"]) }

输出:

./main.go:17:2: cannot assign to struct field u["p"].Sex in map

分析

为啥不能使用u["p"].属性赋值?map[string]Person的value是一个Person结构值,所以当使用u["p"]= person,其实是一个值拷贝过程。

而u["p"]则是一个值引用(注意:所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量是直接指向存在内存中的值。

Golang中引用类型如指针,slice,map,channel,接口,函数等),再说简单点就是:

[值类型]

包括:int、float、bool、string、数组、结构体

值类型变量声明后,不管是否已经赋值,编译器就会为它分配内存,此时该值储存在栈上,比如j=i 时候修改某变量i/j的值不会影响另一个。

[引用类型]

如何精通Go Map的高级使用技巧,实现高效编程?

包括:指针、slice切片、map、chan、interface

变量直接存放的就是一个内存地址值,这个地址值指向的空间存的才是值。所以修改器中一个,另外一个也会修改。

但是需要注意的是引用类型必须申请内存才可以使用,make()是给引用类型申请内存空间。

既然u["p"]是值引用,那么对u["p"].Sex =18的修改当然是不允许滴了。

那要怎么解决赋值问题?这里我整理了两种思路来解决:
解决方案一

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]Person) // 定义一个map ,值为上面Person的结构体 person := Person{"乔峰", 0} u["p"] = person //u["p"].Sex = 18 // 按道理上面u["p"] 已经赋值成了person即Person{"乔峰", 0}的结构体 //解决思路一 tmp := u["p"] //使用临时变量将u["p"]即person 赋值给tmp,该操作为值拷贝 tmp.Sex = 18 //tmp 具备struct u["p"] = tmp //或者person =tmp 将tmp 赋值给u["p"]即person ,该操作为值拷贝复制回去覆盖 fmt.Println(u["p"]) }

以上输出:

{乔峰 18}

上面tmp:=u["p"]是先做一次值拷贝,做出一个tmp副本,然后修改该副本,然后再次发生了一次值拷贝复制回去:u["p"]=tmp,但是这种会在整个过程中发生2次结构体值拷贝,很明显这周搞法性能很差。

自己觉的都看不下去了,有什么更好的实现方案?接下来我们看下思路二:

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) // 注意这里的变化:定义一个map ,值为上面Person的结构体指针 person := Person{"乔峰", 0} u["p"] = &person u["p"].Sex = 18 fmt.Println(u["p"]) }

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

这样我们实际上每次修改的都是指针所指向的Person空间,指针本身是常指针,不能修改,只读属性,但是指向的Person是可以随便修改的,

而且这里我们看到并不需要值拷贝。只是一个指针的赋值。代码明显精简了很多。

Map的遍历赋值

我们来看下下面的代码有什么问题?能说明原因吗?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) // 注意这里的变化:定义一个map ,值为上面Person的结构体指针 //定义Person数组 persons := []Person{ {Name: "乔峰", Sex: 18}, {Name: "鸠摩智", Sex: 23}, {Name: "慕容复", Sex: 22}, } //将数组依次添加到map中 for _, person := range persons { u[person.Name] = &person } //打印map for k, v := range u { fmt.Println(k, "=>", v.Name) } }

遍历结果出现非预期的结果。仔细看,结果是错误的:

乔峰 => 慕容复 鸠摩智 => 慕容复 慕容复 => 慕容复

我们发现map中的3个key都指向数组中最后一个结构体。

分析

for循环中,person是结构体的一个拷贝副本,所以u

[person.Name]=&person

实际上一直都是指向了同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝

怎么改?

package main import "fmt" type Person struct { Name string Sex int } func main() { u := make(map[string]*Person) //定义Person数组 persons := []Person{ {Name: "乔峰", Sex: 18}, {Name: "鸠摩智", Sex: 23}, {Name: "慕容复", Sex: 22}, } // 遍历结构体数组,依次赋值给map for i := 0; i < len(persons); i++ { u[persons[i].Name] = &persons[i] } //打印map for k, v := range u { fmt.Println(k, "=>", v.Name) } }

输出:

鸠摩智 => 鸠摩智 慕容复 => 慕容复 乔峰 => 乔峰

通过上面的示例,希望能让大家少走弯路,避免踩坑。

无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。