脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go语言深度拷贝工具deepcopy的使用教程

Go语言深度拷贝工具deepcopy的使用教程

2022-11-20 13:55Go学堂 Golang

今天给大家推荐的工具是deepcopy,一个可以对指针、接口、切片、结构体、Map都能进行深拷贝的工具,感兴趣的小伙伴快跟随小编一起学习学习

今天给大家推荐的工具是deepcopy,一个可以对指针、接口、切片、结构体、Map都能进行深拷贝的工具。在Go中需要对一个变量进行拷贝时分浅拷贝和深拷贝。浅拷贝就是拷贝后就是无论改变新值还是原值都对对另一个产生影响,比如切片。而深拷贝则是将目标值完全拷贝一份,消除这种影响。

实现原理分析

深拷贝的实现原理本质上是通过反射实现。通过将源对象转换成接口,再对接口通过反射判断其类型,进而进行深度拷贝。如下就是该包的完全实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package deepcopy
 
import (
    "reflect"
    "time"
)
 
// Interface for delegating copy process to type
type Interface interface {
    DeepCopy() interface{}
}
 
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
func Iface(iface interface{}) interface{} {
    return Copy(iface)
}
 
// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}.  The returned value will need to be asserted to the
// correct type.
func Copy(src interface{}) interface{} {
    if src == nil {
        return nil
    }
 
    // Make the interface a reflect.Value
    original := reflect.ValueOf(src)
 
    // Make a copy of the same type as the original.
    cpy := reflect.New(original.Type()).Elem()
 
    // Recursively copy the original.
    copyRecursive(original, cpy)
 
    // Return the copy as an interface.
    return cpy.Interface()
}
 
// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
    // check for implement deepcopy.Interface
    if original.CanInterface() {
        if copier, ok := original.Interface().(Interface); ok {
            cpy.Set(reflect.ValueOf(copier.DeepCopy()))
            return
        }
    }
 
    // handle according to original's Kind
    switch original.Kind() {
    case reflect.Ptr:
        // Get the actual value being pointed to.
        originalValue := original.Elem()
 
        // if  it isn't valid, return.
        if !originalValue.IsValid() {
            return
        }
        cpy.Set(reflect.New(originalValue.Type()))
        copyRecursive(originalValue, cpy.Elem())
 
    case reflect.Interface:
        // If this is a nil, don't do anything
        if original.IsNil() {
            return
        }
        // Get the value for the interface, not the pointer.
        originalValue := original.Elem()
 
        // Get the value by calling Elem().
        copyValue := reflect.New(originalValue.Type()).Elem()
        copyRecursive(originalValue, copyValue)
        cpy.Set(copyValue)
 
    case reflect.Struct:
        t, ok := original.Interface().(time.Time)
        if ok {
            cpy.Set(reflect.ValueOf(t))
            return
        }
        // Go through each field of the struct and copy it.
        for i := 0; i < original.NumField(); i++ {
            // The Type's StructField for a given field is checked to see if StructField.PkgPath
            // is set to determine if the field is exported or not because CanSet() returns false
            // for settable fields.  I'm not sure why.  -mohae
            if original.Type().Field(i).PkgPath != "" {
                continue
            }
            copyRecursive(original.Field(i), cpy.Field(i))
        }
 
    case reflect.Slice:
        if original.IsNil() {
            return
        }
        // Make a new slice and copy each element.
        cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
        for i := 0; i < original.Len(); i++ {
            copyRecursive(original.Index(i), cpy.Index(i))
        }
 
    case reflect.Map:
        if original.IsNil() {
            return
        }
        cpy.Set(reflect.MakeMap(original.Type()))
        for _, key := range original.MapKeys() {
            originalValue := original.MapIndex(key)
            copyValue := reflect.New(originalValue.Type()).Elem()
            copyRecursive(originalValue, copyValue)
            copyKey := Copy(key.Interface())
            cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
        }
 
    default:
        cpy.Set(original)
    }
}

基本使用

拷贝切片

?
1
2
3
4
5
a := []int{1,2,3}
dst := deepcopy.Copy(a)
a1 := dst.([]int)
a1[0] = 2
fmt.Println(a, a1) //a:[1 2 3] a1:[2 2 3]

拷贝map

?
1
2
3
4
5
6
7
8
a := make(map[string]int)
a["k1"] = 1
a["k2"] = 2
a["k3"] = 3
dst := deepcopy.Copy(a)
a1 := dst.(map[string]int)
a1["k1"] = 10
fmt.Println(a, a1) //a:map[k1:1 k2:2 k3:3] a1:map[k1:10 k2:2 k3:3]

更多项目详情请查看如下链接。

开源项目地址:https://github.com/mohae/deepcopy

到此这篇关于Go语言深度拷贝工具deepcopy的使用教程的文章就介绍到这了,更多相关Go深度拷贝deepcopy内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://juejin.cn/post/7143030425839992863

延伸 · 阅读

精彩推荐
  • Golang使用dep 配置golang 开发环境的操作方法

    使用dep 配置golang 开发环境的操作方法

    下面小编就为大家带来一篇使用dep 配置golang 开发环境的操作方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    jingxian2632020-05-08
  • Golanggolang fmt格式“占位符”的实例用法详解

    golang fmt格式“占位符”的实例用法详解

    在本篇文章里小编给大家整理的是一篇关于golang fmt格式“占位符”的实例用法详解内容,有兴趣的朋友们可以学习下。...

    studygolang6142021-08-13
  • Golang深入理解Golang make和new的区别及实现原理

    深入理解Golang make和new的区别及实现原理

    在Go语言中,有两个比较雷同的内置函数,分别是new和make方法,二者都可以用来分配内存,那他们有什么区别呢?下面我们就从底层来分析一下二者的不同...

    1个俗人9852022-11-04
  • Golanggo module化 import 调用本地模块 tidy的方法

    go module化 import 调用本地模块 tidy的方法

    这篇文章主要介绍了go module化 import 调用本地模块 tidy的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友...

    Hoto Cocoa10802022-11-17
  • GolangGo处理json数据方法详解(Marshal,UnMarshal)

    Go处理json数据方法详解(Marshal,UnMarshal)

    这篇文章主要介绍了Go处理json数据的方法详解,Marshal(),UnMarshal(),需要的朋友可以参考下...

    骏马金龙5002022-09-23
  • GolangGolang 实现interface类型转string类型

    Golang 实现interface类型转string类型

    这篇文章主要介绍了Golang 实现interface类型转string类型的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    一条小码农10782021-05-28
  • GolangGo1.18都出泛型了速来围观

    Go1.18都出泛型了速来围观

    泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型,本文通过例子给大家介绍下如何使用...

    jiangxiaoju8382022-09-07
  • Golang如何给 Go 提性能优化的 pr

    如何给 Go 提性能优化的 pr

    之前写了一篇《成为 Go Contributor》 的文章,讲了如何给 Go 提一个 typo 的 pr,以此熟悉整个流程。当然,离真正的 Contributor 还差得远。开课前曹大在 Go 夜读...

    码农桃花源10222021-08-04