在编写 Go 代码的时候,我们经常会遇到需要复制一个 slice 或者 map 或者是 struct 等数据类型的对象的时候,但是,有时会出现我们觉得意外的情况,例如下面这段代码:

你可以先猜测一下这段代码的输出是什么,然后再看看下面这个真实的输出:

slice02 =  hello liqiang.io
slice02 =  hi liqiang.io

这个问题其实在其他很多程序语言中也是会出现的,这就是很常见的深复制和浅复制的问题,那么对于这个问题最简单的办法就是自己来构建对象,例如这么来操作:

slice02 =  hello liqiang.io
slice02 =  hello liqiang.io

虽然这种方式是有效且精准的,但是,却不够高效,所以 Go 语言内置有一个很好用的函数 copy,可以帮助我们快速得做这种事情:

但是,效率虽然上来了,但是又缺乏了通用性,如果想要通用性,截至到目前为止 Go 语言自身还没有很好的方式,我平时使用用的一个比较方便的方式为将数据结构 Dump 成一个 json(bson)等序列化数据,然后再以新的结构体形式 load 回来,这虽然具有了很好的通用性,但是却也牺牲了性能,但是不失为一个不错的处理方式:

除了这种方式之外,第三方还有一个库也不错,使用起来可能也挺方便的:copier,它是通过 Go 语言中的反射机制来实现的,不过它的一个缺点就是只能 copy struct,对于带 slice 的对象是会出现浅拷贝的情况:

这一段的输出为:

slice02 =  hello liqiang.io
slice02 =  hi liqiang.io

json vs copier

为了比较 copier 和 json 的性能,这里我做了一个简单的 benchmark,看看效果:

其实结果应该会挺明显的:

在简单数据类型的情况下,性能差距差了一个数量级,这个从原理上是可以理解的,毕竟 json 的 marshal 和 unmarshal 过程其实也是一个反射的过程,但是比 copier 还多了一个转换成 data 的消耗。