设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间00 现在时间00 缓存数据 对自己狠一点,逼自己努力,再过几年你将会感谢今天发狠的自己、恨透今天懒惰自卑的自己。晚安!

对自己狠一点,逼自己努力,再过几年你将会感谢今天发狠的自己、恨透今天懒惰自卑的自己。晚安!

查看: 2280|回复: 5

Go 修改map slice array元素值操作

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:230
  • 打卡月天数:0
  • 打卡总奖励:3632
  • 最近打卡:2025-11-15 13:58:33
等级头衔

等級:晓枫资讯-上等兵

在线时间
7 小时

积分成就
威望
0
贡献
542
主题
522
精华
1
金钱
5286
积分
1132
注册时间
2023-1-8
最后登录
2025-11-15

发表于 2023-4-17 16:36:53 | 显示全部楼层 |阅读模式
在“range” 语句中生成的数据的值其实是集合元素的拷贝。它们不是原有元素的引用。
这就意味着更新这些值将不会修改原来的数据。
我们来直接看段示例:
  1. package main
  2. import "fmt"
  3. func main() {
  4. data := []int{1, 2, 3}
  5. for _, v := range data {
  6.   v *= 10 //原始元素未更改
  7. }
  8. fmt.Println("data:", data) //输出 data: [1 2 3]
  9. }
复制代码
如果我们需要更新原有集合中的数据,使用索引操作符来获得数据即可:
  1. package main
  2. import "fmt"
  3. func main() {
  4. data := []int{1, 2, 3}
  5. for i, _ := range data {
  6.   data[i] *= 10
  7. }
  8. fmt.Println("data:", data) //输出 data: [10 20 30]
  9. }
复制代码
好,重点来了!重点来了!重点来了!重要的话说三遍,大部分博友们可能会踩坑.
这里我提前总结下:

多个slice可以引用同一个数据。比如,当你从一个已有的slice创建一个新的slice时(比如通过索引截取),这就会发生。
如果你的应用功能需要这种行为,那么你将需要留意下slice的"坑"。
在某些情况下,在一个slice中添加新的数据,在原有数组无法保持更多新的数据时,将导致分配一个新的数组。
而其他的slice还指向老的数组(或者是老的数据)。
  1. package main
  2. import "fmt"
  3. func main() {
  4. s1 := []int{1, 2, 3}
  5. fmt.Println(len(s1), cap(s1), s1) //输出 3 3 [1 2 3]
  6. s2 := s1[1:] //索引从第二个元素截取开始
  7. fmt.Println(len(s2), cap(s2), s2) //输出 2 2 [2 3]
  8. for i := range s2 {
  9.   s2[i] += 20
  10. }
  11. //仍然引用同一数组
  12. fmt.Println(s1) //s1 在s2修改了后面2个元素,所以s1也是更新了。输出 [1 22 23]
  13. fmt.Println(s2) //输出 [22 23]
  14. s2 = append(s2, 4) // 注意s2的容量是2,追加新元素后将导致分配一个新的数组 [22 23 4]
  15. for i := range s2 {
  16.   s2[i] += 10
  17. }
  18. //s1 仍然是更新后的历史老数据
  19. fmt.Println(s1) //输出 [1 22 23]
  20. fmt.Println(s2) //输出 [32 33 14]
  21. }
复制代码
所以,大家在使用中特别注意。容量不足,追加新元素不影响历史数据。因为重新分配了变量了。
另外,继续聊下高级一点滴技巧:
使用指针接收方法的值

只要值是可取址的,那在这个值上调用指针接收方法是没问题的。
然而并不是所有的变量是可取址的。Map的元素就不是。通过interface引用的变量也不是。我们接着看下面一段代码:
  1. package main
  2. import "fmt"
  3. type user struct {
  4. name string
  5. }
  6. func (p *user) print() {
  7. fmt.Println("排名:", p.name)
  8. }
  9. type printer interface {
  10. print()
  11. }
  12. func main() {
  13. u := user{"乔峰"}
  14. u.print()     // 输出 排名: 乔峰
  15. var in printer = user{"鸠摩智"} //error
  16. in.print()
  17. m := map[string]user{"one": user{"风清扬"}}
  18. m["one"].print() //error
  19. }
复制代码
输出:
  1. cannot use user literal (type user) as type printer in assignment:
  2.   user does not implement printer (print method has pointer receiver)
  3. cannot call pointer method on m["one"]
  4. cannot take the address of m["one"]
复制代码
大致意思是:不能在赋值中使用数据文本(类型数据)作为类型指针,user未执行指针调用(指针方法具有指针接收器),
无法对m[“one”]调用指针方法,不能取m的地址[“one”]。
上面我们看到有一个struct值的map,我们无法更新单个的struct值。比如错误的代码:
  1. package main
  2. type user struct {
  3. name string
  4. }
  5. func main() {
  6. m := map[string]user{"one": {"乔峰"}}
  7. m["one"].name = "风清扬" //输出 cannot assign to struct field m["one"].name in map
  8. }
复制代码
错误意思是:在map中,无法分配给结构字段m["one"].name。这个操作无效是因为map元素是无法取址的。
上面我们提到:slice元素是可以取地址滴:
  1. package main
  2. import "fmt"
  3. type user struct {
  4. name string
  5. }
  6. func main() {
  7. one := user{"乔峰"}
  8. u := []user{one}
  9. u[0].name = "风清扬" //ok
  10. fmt.Println(u) //输出: [{风清扬}]
  11. }
复制代码
当然我们还有更好的解决办法:
第一个有效的方法是使用一个临时变量:
  1. package main
  2. import "fmt"
  3. type user struct {
  4. name string
  5. }
  6. func main() {
  7. m := map[string]user{"one": {"乔峰"}}
  8. u := m["one"] //使用临时变量
  9. u.name = "风清扬"
  10. m["one"] = u
  11. fmt.Printf("%v\n", m) //输出: map[one:{风清扬}]
  12. }
复制代码
另一个有效的方法是使用指针的map:
  1. package main
  2. import "fmt"
  3. type user struct {
  4. name string
  5. }
  6. func main() {
  7. m := map[string]*user{"one": {"乔峰"}}
  8. m["one"].name = "风清扬" //ok
  9. fmt.Println(m["one"]) //输出: &{风清扬}
  10. }
复制代码
说到这里,顺便再提一下。继续看下面一段代码:
  1. package main
  2. import "fmt"
  3. type user struct {
  4. name string
  5. }
  6. func main() {
  7. m := map[string]*user{"one": {"乔峰"}}
  8. m["two"].name = "鸠摩智" //新增自定义键名值
  9. fmt.Println(m["two"]) //error
  10. }
复制代码
输出:
  1. panic: runtime error: invalid memory address or nil pointer dereference
复制代码
无效的内存地址或取消引用空指针?原因在于Go无法动态给结构体添加字段,我们可以间接使用make(map[string]interface{})实现。
好吧,就说这么多了,有不足之处欢迎广大博友留言指正。。。。。。。
补充:golang 中map 和slice 索引速度比较
主文件
  1. package main
  2. var max = 100
  3. var Slice = make([]int, max+10)
  4. var Map = make(map[int]int)
  5. func init() {
  6. for i := 0; i < max; i++ {
  7. Slice[i] = i
  8. Map[i] = i
  9. }
  10. }
  11. // 查找算法可以优化,本文对于常用无序查找做比较
  12. func SearchSlice(i int) int {
  13. for _, v := range Slice {
  14. if v == i {
  15. return v
  16. }
  17. }
  18. return -1
  19. }
  20. func SearchMap(i int) int {
  21. return Map[i]
  22. }
复制代码
测试文件
  1. package main
  2. import "testing"
  3. func BenchmarkSearchMap(b *testing.B) {
  4. for i := 0; i < b.N; i++ {
  5. _ = SearchMap(i % max)
  6. }
  7. }
  8. func BenchmarkSearchSlice(b *testing.B) {
  9. for i := 0; i < b.N; i++ {
  10. _ = SearchSlice(i % max)
  11. }
  12. }
  13. func BenchmarkSlice(b *testing.B) {
  14. for i := 0; i < b.N; i++ {
  15. _ = Slice[i%max]
  16. }
  17. }
复制代码
测试结果

max = 100
  1. BenchmarkSearchMap-16   94148293    12.7 ns/op    0 B/op   0 allocs/op
  2. BenchmarkSearchSlice-16   49473447    23.6 ns/op    0 B/op   0 allocs/op
  3. BenchmarkSlice-16    187461336    6.46 ns/op   0 B/op   0 allocs/op
复制代码
max = 10000
  1. BenchmarkSearchMap-16   43147364    27.6 ns/op    0 B/op   0 allocs/op
  2. BenchmarkSearchSlice-16   968623    1159 ns/op    0 B/op   0 allocs/op
  3. BenchmarkSlice-16    187649472    6.42 ns/op   0 B/op   0 allocs/op
复制代码
Max = 1000000
  1. BenchmarkSearchMap-16     15015690    90.1 ns/op    0 B/op   0 allocs/op
  2. BenchmarkSearchSlice-16     441436   104242 ns/op    0 B/op   0 allocs/op
  3. BenchmarkSlice-16      182620702    6.58 ns/op   0 B/op   0 allocs/op
复制代码
在一些特定优化条件下,可以尝试用slice,效果会比map好,比如把10 6级的查找优化成3级102查找, 对于一些结构体,可以根据某些特征分类或预先根据特征值排序。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。如有错误或未考虑完全的地方,望不吝赐教。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
21
积分
22
注册时间
2022-12-24
最后登录
2022-12-24

发表于 2023-6-7 10:32:44 | 显示全部楼层
感谢分享~~~~学习学习~~~~~
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 打卡月天数:0
  • 打卡总奖励:38
  • 最近打卡:2024-06-18 02:14:20
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
54
积分
12
注册时间
2023-6-17
最后登录
2024-6-18

发表于 2023-7-18 10:48:11 | 显示全部楼层
感谢楼主分享~~~~~
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
18
积分
16
注册时间
2022-12-25
最后登录
2022-12-25

发表于 2024-10-9 10:11:12 | 显示全部楼层
顶顶更健康!!!
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
12
积分
4
注册时间
2023-8-20
最后登录
2023-8-20

发表于 2024-11-10 18:40:21 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
21
积分
22
注册时间
2022-12-29
最后登录
2022-12-29

发表于 2025-1-19 10:37:33 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼
4楼
5楼
6楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表