Go克隆几种方式

简介: Go克隆几种方式

Go克隆几种方式


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

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

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)
}


测试用例


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)
}


执行结果:


=== RUN   TestClone1
2021/12/23 10:37:56 &{A:hello world B:34 C:[1213 1312]}
--- PASS: TestClone1 (0.00s)
PASS


反射实现深度拷贝


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

/深度克隆,可以克隆任意数据类型
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
 }
}


测试用例


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)
}


执行结果:


=== RUN   TestDeepClone
&{hello world 34 [1213 1312]}
--- PASS: TestDeepClone (0.00s)
PASS


借助包深拷贝


"github.com/mohae/deepcopy"

使用方式如下:


dst := deepcopy.Copy(src)


测试用例


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)
}


测试结果:


=== RUN   TestDeepCopy
&{ 0 []}
--- PASS: TestDeepCopy (0.00s)
PASS


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

换个用例


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)
}


执行结果:


=== RUN   TestDeepCopy
&{xiaomng 100}
--- PASS: TestDeepCopy (0.00s)
PASS


总结

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

相关文章
|
2月前
|
Unix Linux Go
go基础-20.部署
go基础-20.部署
|
4月前
|
Linux Shell Go
如何构建和安装 Go 程序
如何构建和安装 Go 程序
55 1
|
4月前
|
搜索推荐 Go UED
揭秘 Go 中的模板:一份全面而广泛的指南
揭秘 Go 中的模板:一份全面而广泛的指南
|
4月前
|
算法 搜索推荐 Unix
快速指南: Go 1.19功能
快速指南: Go 1.19功能
|
设计模式 算法 Go
Go的模板方式模式
模板方法模式是一种行为设计模式,它在一个抽象类中定义了一个算法的骨架,允许子类为算法的某些步骤提供具体实现。它促进了代码的重用,并为一组相关算法定义了一个通用的结构。
57 0
|
Go
Go 删除文件方式
Go 删除文件方式
133 0
|
Linux Go iOS开发
Hello World!1分钟配置好你的Go环境
Hello World!1分钟配置好你的Go环境
187 0
YI
|
缓存 IDE 算法
Go学习笔记01|Go项目的创建与运行
Go学习笔记01|Go项目的创建与运行
YI
511 0
|
缓存 Go
一文掌握 Go 文件的写入操作
本文先是对 File.Write、File.WriteString、File.WriteAt 进行介绍,通过例子演示它们的使用方式;然后介绍 File.Seek,说明了它的用法;最后引出 bufio.NewWriter、Writer.WriteString、Writer.Flush,使用它们代替 File 结构体里的写入方法,可以不用频繁操作磁盘,提高写入效率。
330 1
一文掌握 Go 文件的写入操作
|
存储 安全 算法
Go源代码解析-sema.go文件
Go源代码解析-sema.go文件
85 0