Go语言Map拷贝陷阱、Slice更新陷阱

开发
map 其实是不能拷贝的,如果想要拷贝一个 map ,只有一种办法就是循环赋值,就像这样,如果 map 中有指针,还要考虑深拷贝的过程。

 [[410263]]

map 可以拷贝吗?

map 其实是不能拷贝的,如果想要拷贝一个 map ,只有一种办法就是循环赋值,就像这样

  1. originalMap := make(map[string]int
  2. originalMap["one"] = 1 
  3. originalMap["two"] = 2 
  4.  
  5. // Create the target map 
  6. targetMap := make(map[string]int
  7.  
  8. // Copy from the original map to the target map 
  9. for key, value := range originalMap { 
  10.     targetMap[key] = value 

如果 map 中有指针,还要考虑深拷贝的过程

  1. originalMap := make(map[string]*int
  2. var num int = 1 
  3. originalMap["one"] = &num 
  4.  
  5. // Create the target map 
  6. targetMap := make(map[string]*int
  7.  
  8. // Copy from the original map to the target map 
  9. for key, value := range originalMap { 
  10. var tmpNum int = *value 
  11.     targetMap[key] = &tmpNum 

如果想要更新 map 中的value,可以通过赋值来进行操作

  1. map["one"] = 1 

但如果 value 是一个结构体,可以直接替换结构体,但无法更新结构体内部的值

  1. originalMap := make(map[string]Person) 
  2. originalMap["minibear2333"] = Person{age: 26} 
  3. originalMap["minibear2333"].age = 5 

你可以 试下源码函数[脚注1] 会报这个错误

  • Cannot assign to originalMap["minibear2333"].age”

问题链接 issue-3117[脚注2] , 其中 ianlancetaylor[脚注3] 的回答很好的解释了这一点

简单来说就是map不是一个并发安全的结构,所以,并不能修改他在结构体中的值。

这如果目前的形式不能修改的话,就面临两种选择,

  • 1.修改原来的设计;
  • 2.想办法让map中的成员变量可以修改,

因为懒得该这个结构体,就选择了方法2

要么创建个临时变量,做拷贝,像这样

  1. tmp := m["foo"
  2. tmp.x = 4 
  3. m["foo"] = tmp 

要么直接用指针,比较方便

  1. originalPointMap := make(map[string]*Person) 
  2. originalPointMap["minibear2333"] = &Person{age: 26} 
  3. originalPointMap["minibear2333"].age = 5 

slice复制陷阱

切片有一种方式复制方式,比较快速

  1. slice3 :=  slice2[:] 

但是有一种致命的缺点,这是浅拷贝,slice3和slice2是同一个切片,无论改动哪个,另一个都会产生变化。

可能这么说你还是不能加深理解。在源码bytes.buffer[脚注4]中出现了这一段

  1. func (b *Buffer) Bytes() []byte { 
  2.     return b.buf[b.off:]  

我们在读入读出输入流的时候,极易出现这样的问题

下面的例子,使用abc模拟读入内容,修改返回值内容

  1. buffer := bytes.NewBuffer(make([]byte, 0, 100)) 
  2. buffer.Write([]byte("abc")) 
  3. resBytes := buffer.Bytes() 
  4. fmt.Printf("%s \n", resBytes) 
  5. resBytes[0] = 'd' 
  6. fmt.Printf("%s \n", resBytes) 
  7. fmt.Printf("%s \n", buffer.Bytes()) 

输出,可以看出会影响到原切片内容

  1. abc 
  2. dbc 
  3. dbc 

这种情况在并发使用的时候尤为危险,特别是流式读写的时候容易出现上一次没处理完成,下一次的数据覆盖写入的错乱情况

脚注

直接点击阅读原文跳转

  • 试下源码函数 updateMapValue:https://github.com/golang-minibear2333/golang/blob/master/2.func-containers/2.4-map/map1.go#L89
  • issue-3117:https://github.com/golang/go/issues/3117
  • ianlancetaylor:https://github.com/golang/go/issues/3117#issuecomment-430632750
  • 源码 https://github.com/golang/go/blob/cb4cd9e17753b5cd8ee4cd5b1f23d46241b485f1/src/bytes/buffer.go#L54

本文转载自微信公众号「机智的程序员小熊」,可以通过以下二维码关注。转载本文请联系机智的程序员小熊公众号。

 

责任编辑:武晓燕 来源: 机智的程序员小熊
相关推荐

2023-06-26 00:03:55

Go语言类型

2023-07-03 00:44:26

Go语言MySQL

2012-06-15 09:56:40

2024-01-07 23:11:16

defer​Go语言

2023-12-04 18:31:59

C语言函数

2022-02-09 16:02:26

Go 语言ArraySlice

2014-07-29 09:16:14

Fragment

2010-07-14 17:03:52

编程语言

2022-10-14 09:12:59

参数Python类型

2024-06-12 08:54:49

Go切片参数

2010-10-27 11:04:01

招聘

2010-07-28 13:31:10

Flex数据绑定

2010-07-19 15:49:22

求职陷阱

2024-04-29 14:39:20

2011-07-14 10:23:33

C语言

2021-03-01 15:52:14

开源开源软件陷阱

2023-05-09 07:09:02

2024-09-03 09:45:36

2021-04-22 11:22:12

云计算数据迁移混合云

2010-03-03 12:37:04

慎防陷阱
点赞
收藏

51CTO技术栈公众号