go - golang中的深度复制数据结构

标签 go struct deep-copy

我想复制一个数据结构的实例。由于 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:对于那些可能会说“不需要这样的功能”的人,我是将一些复杂的状态和一些数据结构分配给并行计算线程,他们直接使用状态,不能相互干扰。

最佳答案

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

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

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

注释 #1:

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

关于go - golang中的深度复制数据结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56355212/

相关文章:

python - 在 Python 中复制嵌套列表

javascript - 检测复杂对象的变化

go - 使用 Go 语言简单 HTTP 服务器打印到日志

go - 倒序转换整数以倒序生成IP

Julia 中的结构文字

c# - 尝试使用 char * 数据编码结构,但数据为空

Swift 结构类型集

Java 泛型 : method signature for (deep copy of) generic Maps

go - 使用golang在redis中存储单次使用记录的最佳方法

go - Golang 上的包导入错误