前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go克隆几种方式

Go克隆几种方式

作者头像
王小明_HIT
发布2021-12-25 16:29:11
1.6K0
发布2021-12-25 16:29:11
举报
文章被收录于专栏:程序员奇点程序员奇点

Go克隆几种方式

序列化的方式实现深度拷贝

最简单的方式是基于序列化和反序列化来实现对象的深度复制:

代码语言:javascript
复制
func deepCopy(dst, src interface{}) error {
    var buf bytes.Buffer
    if err := gob.NewEncoder(&buf).Encode(src); err != nil {
        return err
    }
    return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

测试用例

代码语言:javascript
复制
import (
 "bytes"
 "encoding/gob"
 "encoding/json"
 "fmt"
 "log"
 "reflect"
 "testing"

 "github.com/mohae/deepcopy"
)

type Basics struct {
 A string
 B int
 C []string
}
func TestClone1(t *testing.T) {
 var src2 = &Basics{
  A: "hello world",
  B: 34,
  C: []string{"1213", "1312"},
 }

 dst := new(Basics)

 if err := deepCopy(dst, src2); err != nil {
  log.Fatal(err)
 }
 log.Printf("%+v", dst)
}

执行结果:

代码语言:javascript
复制
=== RUN   TestClone1
2021/12/23 10:37:56 &{A:hello world B:34 C:[1213 1312]}
--- PASS: TestClone1 (0.00s)
PASS
反射实现深度拷贝

深度复制可以基于reflect包的反射机制完成, 但是全部重头手写的话会很繁琐.

代码语言:javascript
复制

/深度克隆,可以克隆任意数据类型
func DeepClone(src interface{}) (interface{}, error) {
 typ := reflect.TypeOf(src)
 if typ.Kind() == reflect.Ptr {
  typ = typ.Elem()
  dst := reflect.New(typ).Elem()
  b, _ := json.Marshal(src)
  if err := json.Unmarshal(b, dst.Addr().Interface()); err != nil {
   return nil, err
  }
  return dst.Addr().Interface(), nil
 } else {
  dst := reflect.New(typ).Elem()
  b, _ := json.Marshal(src)
  if err := json.Unmarshal(b, dst.Addr().Interface()); err != nil {
   return nil, err
  }
  return dst.Interface(), nil
 }
}
测试用例
代码语言:javascript
复制
import (
 "bytes"
 "encoding/gob"
 "encoding/json"
 "fmt"
 "log"
 "reflect"
 "testing"

 "github.com/mohae/deepcopy"
)

type Basics struct {
 A string
 B int
 C []string
}
func TestDeepClone(t *testing.T) {
 var src = &Basics{
  A: "hello world",
  B: 34,
  C: []string{"1213", "1312"},
 }
 dst, _ := DeepClone(src)
 fmt.Println(dst)
}

执行结果:

代码语言:javascript
复制
=== RUN   TestDeepClone
&{hello world 34 [1213 1312]}
--- PASS: TestDeepClone (0.00s)
PASS
借助包深拷贝

"github.com/mohae/deepcopy"

使用方式如下:

代码语言:javascript
复制
dst := deepcopy.Copy(src)

测试用例

代码语言:javascript
复制
import "github.com/mohae/deepcopy"

type BasicsSmall struct {
 a string
 b int
 c []string
}
func TestDeepCopy(t *testing.T) {
 //src := &User{Name: "xiaomng", Age: 100}
 var src = &BasicsSmall{
  a: "hello world",
  b: 34,
  c: []string{"1213", "1312"},
 }
 dst := deepcopy.Copy(src)
 fmt.Println(dst)
}

测试结果:

代码语言:javascript
复制
=== RUN   TestDeepCopy
&{ 0 []}
--- PASS: TestDeepCopy (0.00s)
PASS

为啥会出现上面的结果,因为 struct 结构,首字母都是小写的!!!

换个用例

代码语言:javascript
复制
import "github.com/mohae/deepcopy"
type User struct {
 Name string // 小写变量,不能被deepCopy函数拷贝成功
 Age  int
}

func TestDeepCopy(t *testing.T) {
 src := &User{Name: "xiaomng", Age: 100}
 //var src = &BasicsSmall{
 // a: "hello world",
 // b: 34,
 // c: []string{"1213", "1312"},
 //}
 dst := deepcopy.Copy(src)
 fmt.Println(dst)
}

执行结果:

代码语言:javascript
复制
=== RUN   TestDeepCopy
&{xiaomng 100}
--- PASS: TestDeepCopy (0.00s)
PASS

总结

上述拷贝有个问题,结构体中有小写成员变量时,上述方式无效。

再给个代码例子吧:

代码语言:javascript
复制
package utils

import (
    "encoding/json"
    "reflect"
)

//浅克隆,可以克隆任意数据类型,对指针类型子元素无法克隆
//获取类型:如果类型是指针类型,需要使用Elem()获取对象实际类型
//获取实际值:如果值是指针类型,需要使用Elem()获取实际数据
//说白了,Elem()就是获取反射数据的实际类型和实际值
func Clone(src interface{}) interface{} {
    typ := reflect.TypeOf(src)
    if typ.Kind() == reflect.Ptr { //如果是指针类型
        typ = typ.Elem()               //获取源实际类型(否则为指针类型)
        dst := reflect.New(typ).Elem() //创建对象
        data := reflect.ValueOf(src)   //源数据值
        data = data.Elem()             //源数据实际值(否则为指针)
        dst.Set(data)                  //设置数据
        dst = dst.Addr()               //创建对象的地址(否则返回值)
        return dst.Interface()         //返回地址
    } else {
        dst := reflect.New(typ).Elem() //创建对象
        data := reflect.ValueOf(src)   //源数据值
        dst.Set(data)                  //设置数据
        return dst.Interface()         //返回
    }
}

//深度克隆,可以克隆任意数据类型
func DeepClone(src interface{}) interface{} {
    typ := reflect.TypeOf(src)
    if typ.Kind() == reflect.Ptr { //如果是指针类型
        typ = typ.Elem()                          //获取源实际类型(否则为指针类型)
        dst := reflect.New(typ).Elem()            //创建对象
        b, _ := json.Marshal(src)                 //导出json
        json.Unmarshal(b, dst.Addr().Interface()) //json序列化
        return dst.Addr().Interface()             //返回指针
    } else {
        dst := reflect.New(typ).Elem()            //创建对象
        b, _ := json.Marshal(src)                 //导出json
        json.Unmarshal(b, dst.Addr().Interface()) //json序列化
        return dst.Interface()                    //返回值
    }
}

参考资料

  • https://houbb.github.io/2019/01/09/java-deep-copy
  • https://www.codenong.com/cs105839518/
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员奇点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Go克隆几种方式
    • 序列化的方式实现深度拷贝
      • 反射实现深度拷贝
        • 测试用例
          • 借助包深拷贝
          • 总结
          • 参考资料
          相关产品与服务
          文件存储
          文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档