golang 中的深度复制数据结构

     2023-02-18     34

关键词:

【中文标题】golang 中的深度复制数据结构【英文标题】:Deep copying data structures in golang 【发布时间】:2019-10-14 17:59:49 【问题描述】:

我想复制一个数据结构的实例。由于 go 没有任何内置函数,因此我使用的是第三方库:https://github.com/emirpasic/gods

例如,我可以尝试使用带有哈希集的深拷贝。

var c, d hashset.Set
c = *hashset.New()
c.Add(1)
deepcopy.Copy(d, c)
c.Add(2)
fmt.Println(c.Contains(2))
fmt.Println(d.Contains(2))
fmt.Println(c.Contains(1))
fmt.Println(d.Contains(1))

但是,哈希集的内容根本没有被复制。我知道深拷贝模块不能复制未导出的值,但是由于库中没有内置的“复制构造函数”,这是否意味着在不修改其代码的情况下无法使用库完全复制数据结构实例? (我研究过的其他一些库也出现了类似的问题)。

我是 golang 的新手,感觉不对,因为类似的事情可以很容易地实现,例如在 C++ 中。我知道我可以编写自己的版本或修改他们的代码,但工作量比预期的要多,这就是为什么我认为应该有一种惯用的方式。

PS:对于可能会说“不需要这样的功能”的人,我正在将一些具有一些数据结构的复杂状态分发给并行计算线程,它们直接使用这些状态,并且不能相互干扰。

【问题讨论】:

使用包reflect,如果我没记错的话,你应该能够使用unsafe实现你自己的深拷贝,包括未导出的字段。 @mkopriva: 但这听起来像是无中生有:) 能够深度复制所有字段确实很有用。 @mkopriva 您可以使用反射读取未导出的字段,但不能设置它们。见How to clone a structure with unexported field? @icza ***.com/a/43918797/965900(我在最初的评论中确实提到了unsafe @mkopriva 哦,抱歉,在您的第一条评论中没有发现 unsafe 字样。 【参考方案1】:

如果您需要深度复制 protobuf 结构,可以在 https://github.com/golang/protobuf 中使用 proto.Clone,更多帮助请参阅 godoc。

【讨论】:

【参考方案2】:

如果您的结构是可序列化的,您可以将其转换为 JSON 并返回,这对我的用例来说已经足够了。

func CloneMyStruct(orig *model.MyStruct) (*model.MyStruct, error) 
    origJSON, err := json.Marshal(orig)
    if err != nil 
        return nil, err
    

    clone := &model.MyStruct
    if err = json.Unmarshal(origJSON, &clone); err != nil 
        return nil, err
    

    return clone, nil

【讨论】:

【参考方案3】:

不幸与否,在 Go 中没有办法做到这一点。想到的第一个工具是反射(包reflect),但是使用反射只能读取未导出的字段,但不能设置它们。见How to clone a structure with unexported field?

克隆具有未导出字段的结构的唯一方法是使用包unsafe(参见此处的示例:Access unexported fields in golang/reflect?),但正如其名称所说:它是不安全,你应该留下尽可能远离它。使用 unsafe 创建的程序不能保证它们可以继续使用更新的 Go 版本,或者它们在每个平台上的行为都相同。

一般来说在 Go 中支持克隆的唯一且正确的方法是包本身是否支持此类操作。

注意事项 #1:

这并不意味着在某些特定情况下,您不能通过创建新值并手动构建其状态来“模仿”克隆。例如,您可以通过创建一个新映射来克隆 map,遍历原始映射的键值对并将它们设置在新映射中。

注意 #2:

请注意,您可以通过简单地将具有未导出字段的结构的“精确”副本assigning 复制到另一个结构变量(相同类型),这也将正确复制未导出的字段。

就像这个例子:

type person struct 
    Name string
    age  *int


age := 22
p := &person"Bob", &age
fmt.Println(p)

p2 := new(person)
*p2 = *p
fmt.Println(p2)

哪个会输出(在Go Playground上试试):

&Bob 0x414020
&Bob 0x414020

我们甚至可以在不依赖具体类型的情况下使用reflect 进行概括:

type person struct 
    Name string
    age  *int


age := 22
p := &person"Bob", &age
fmt.Println(p)

v := reflect.ValueOf(p).Elem()
vp2 := reflect.New(v.Type())
vp2.Elem().Set(v)
fmt.Println(vp2)

在Go Playground 上试试这个。

但我们不能做的是更改person.age 未导出字段以指向其他内容。没有声明包的帮助,只能是nil或者同一个指针值(指向对象作为原始字段)。

另见相关:Quicker way to deepcopy objects in golang

【讨论】:

【参考方案4】:

如果您使用的包不提供数据结构的复制功能,那么我将编写自己的类型安全复制功能。此外,我不会担心未导出的字段,因为开发人员决定向用户隐藏这些字段一定是有原因的。

在你的例子中:

func NewHashSetCopy(src *hashset.Set) *hashset.Set
    dst := *hashset.New()
    iterator := src.Iter()
    for elem := range iterator 
        dst.Add(elem)
    
    return dst

【讨论】:

golang复制结构体

参考技术AGolang中复制结构体,可以使用赋值语句执行结果可以看出,roger跟mydog在内存中的地址不同。并且对mydog修改属性,对roger没有影响。但是注意,这里的Dog结构体中的属性,都是值类型。如果是引用类型的话,复制的是指... 查看详情

如何将结构深度复制到向量的每个元素

...:2018-08-1019:33:26【问题描述】:您好,我正在尝试对向量中的每个元素进行结构的完整深度复制,但我没有取得太大成功这是我的结构structYUV_BuffermfxFrameSurface1*mSurface;//structorforvideoinfoanddatain 查看详情

golang复制结构(代码片段)

查看详情

如何深度复制一个javascript对象

...量。本以为这就ok了,结果修改原数据,复制出来的变量中的内容,依然发生了变化。在此,整理一下。(大中小)牛略过,仅为帮助新人,聊以解忧。 知识铺垫,值类型,与 查看详情

javascript中的对象深度复制(objectdeepclone)

...ript中并没有直接提供对象复制(ObjectClone)的方法。JavaScript中的赋值,其实并不是复制对象,而是类似`c/c++`中的引用(或指针),因此下面的代码中改变对象b中的元素的时候,也就改变了对象a中的元素。a={k1:1,k2:2,k3:3};b=a;b.k2=4; 如... 查看详情

如何漂亮地打印 Golang 结构? [复制]

】如何漂亮地打印Golang结构?[复制]【英文标题】:HowtoprettyprintaGolangstructure?[duplicate]【发布时间】:2019-10-0801:37:54【问题描述】:我正在解组一个结构,我希望它以格式化的方式打印它。我的代码(https://play.golang.org/p/D0KwGP6Cxa0)... 查看详情

如何增加 Python 中的最大递归深度? [复制]

】如何增加Python中的最大递归深度?[复制]【英文标题】:HowcouldyouincreasethemaximumrecursiondepthinPython?[duplicate]【发布时间】:2016-02-1820:47:59【问题描述】:今天课堂上递归和堆栈溢出的有趣话题,我想知道是否有任何方法可以增加P... 查看详情

通过复制供应商文件夹来构建golang

】通过复制供应商文件夹来构建golang【英文标题】:golangbuildbycopyingvendorfolder【发布时间】:2017-12-2010:06:49【问题描述】:我的项目结构/github.com/user-libraries-services-service-api-signup-Dockerfile-main.go-service-api-second-...-vendorservice-api-signup... 查看详情

python深度复制python中的二维数组(代码片段)

查看详情

golang写时复制是否是原子性的?(代码片段)

建议先阅读下Go汇编语言的入门教程https://go.dev/doc/asm先说一下我这边的一个简化场景吧,有一个定时任务定时从数据库获取数据,也就是对应实例代码中的getNewProject(),获取完数据后,会直接赋给变量projectMap(proje... 查看详情

如果通过 Golang 通道发送,结构是不是实际上在 goroutine 之间复制?

】如果通过Golang通道发送,结构是不是实际上在goroutine之间复制?【英文标题】:IsastructactuallycopiedbetweengoroutinesifsentoveraGolangchannel?如果通过Golang通道发送,结构是否实际上在goroutine之间复制?【发布时间】:2016-06-2102:42:27【问... 查看详情

如何在不替换 ES6/Javascript 中的整个属性的情况下深度复制对象 [重复]

】如何在不替换ES6/Javascript中的整个属性的情况下深度复制对象[重复]【英文标题】:HowtodeeplycopyobjectswithoutreplacingtheentirepropertyinES6/Javascript[duplicate]【发布时间】:2019-01-3009:20:20【问题描述】:我想将所有缺失的字段深度复制到... 查看详情

如何在golang中顺序处理并发请求? [复制]

】如何在golang中顺序处理并发请求?[复制]【英文标题】:Howtohandleconcurrentrequestsequentiallyingolang?[duplicate]【发布时间】:2019-04-1123:03:51【问题描述】:我是Golang的新手,我发现Go频道非常有趣。我的背景来自JavaScript,我想在Go中... 查看详情

如何将sklearn Pipeline结构的结构和数据深度复制到新变量中?

】如何将sklearnPipeline结构的结构和数据深度复制到新变量中?【英文标题】:HowtodeepcopystructureanddataofasklearnPipelinestructureintoanewvariable?【发布时间】:2020-04-0921:27:21【问题描述】:假设我已经定义了一个sklearnPipeline结构。我需要... 查看详情

为啥正常的 for 循环允许为结构字段分配值,而 for range 在 Golang 中不起作用? [复制]

】为啥正常的for循环允许为结构字段分配值,而forrange在Golang中不起作用?[复制]【英文标题】:Whydoesnormalforloopallowsassigningvaluetostructfieldswhileforrangedoesn\'tworkinGolang?[duplicate]为什么正常的for循环允许为结构字段分配值,而forrange在... 查看详情

Golang多维切片拷贝

】Golang多维切片拷贝【英文标题】:Golangmultidimensionalslicecopy【发布时间】:2018-01-0922:43:05【问题描述】:我试图复制多维切片,因为当我更改了复制切片中的元素时,原始切片中的元素也被覆盖了。唯一对我有用的方法是:dupli... 查看详情

golang复合类型(代码片段)

前言上文Golang基本类型中我们介绍了golang基本类型的常见用法,本文将介绍golang中的复合数据类型,常用的复合数据类型有array数组,slice切片,map字典和struct四种。文章目录前言数组Sliceslice基本操作初始化在slice... 查看详情

Golang 中的递归结构反射

】Golang中的递归结构反射【英文标题】:RecursiveStructReflectinGolang【发布时间】:2022-01-2407:27:35【问题描述】:我有一个嵌套结构定义被展平成一个切片(这个假设是不可协商的,我必须处理它):typeelementstructNamestringTypestring//can... 查看详情