当前位置:首页 » 《关注互联网》 » 正文

【Go】Go语言中深拷贝和浅拷贝

23 人参与  2024年10月18日 14:41  分类 : 《关注互联网》  评论

点击全文阅读


在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

?? 养成好习惯,先赞后看哦~??

? 作者简介:景天科技苑
?《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
?《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑

在这里插入图片描述

文章目录

Go语言中深拷贝和浅拷贝一、深拷贝和浅拷贝的基本概念1.1 深拷贝(Deep Copy)1.2 浅拷贝(Shallow Copy) 二、深拷贝和浅拷贝的区别2.1 数据复制2.2 对象关联2.3 内存占用 三、Go语言中深拷贝和浅拷贝的使用场景3.1 深拷贝的使用场景3.2 浅拷贝的使用场景 四、Go语言中深拷贝和浅拷贝的实现方法4.1 浅拷贝的实现示例1:引用类型的浅拷贝示例2:结构体的浅拷贝 4.2 深拷贝的实现4.2.1 使用`json.Marshal`和`json.Unmarshal`4.2.2 递归深拷贝 五、总结

Go语言中深拷贝和浅拷贝

在Go语言中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种常见的对象复制方式,它们的主要区别在于是否真正获取到了被拷贝对象的单独掌控权,从而避免互相影响的问题。本文将结合实际案例,详细阐述Go语言中深拷贝和浅拷贝的概念、区别、使用场景及具体实现方法。

一、深拷贝和浅拷贝的基本概念

1.1 深拷贝(Deep Copy)

深拷贝是指创建一个新对象,并递归地复制原对象中的所有数据(包括子对象),使得新对象与原对象在内存中是完全独立的。修改新对象不会影响原对象,反之亦然。深拷贝保证了对象的完全独立性和数据的完整性。

1.2 浅拷贝(Shallow Copy)

浅拷贝只是复制了对象本身,而没有复制其所引用的子对象。换句话说,浅拷贝仅仅复制了对象的引用(或指针),使得原对象和新对象共享同一块内存空间。因此,修改新对象中的引用类型数据会影响到原对象,反之亦然。

二、深拷贝和浅拷贝的区别

2.1 数据复制

深拷贝:复制所有数据,包括值和子对象。浅拷贝:只复制对象的引用,不复制子对象。

2.2 对象关联

深拷贝:新对象与原对象完全独立,修改其中一个不会影响另一个。浅拷贝:新对象与原对象共享部分数据(如子对象),修改其中一个可能会影响另一个。

2.3 内存占用

深拷贝:由于复制了所有数据,因此可能会占用更多内存。浅拷贝:只复制引用,占用内存较少。

三、Go语言中深拷贝和浅拷贝的使用场景

3.1 深拷贝的使用场景

当需要创建一个独立的对象,并且不希望修改原始对象时,应使用深拷贝。例如,处理敏感数据时,为了避免数据泄露,需要复制一份新的数据进行操作。在并发编程中,当多个goroutine需要操作同一份数据的副本时,为了防止竞态条件,应使用深拷贝。

3.2 浅拷贝的使用场景

当需要创建一个对象的副本,并且希望修改副本时,可以使用浅拷贝。这样,修改副本的同时也会影响到原对象,这在某些场景下是需要的。在某些性能敏感的场景下,为了减少内存占用和提高性能,可以使用浅拷贝。

四、Go语言中深拷贝和浅拷贝的实现方法

4.1 浅拷贝的实现

在Go语言中,值类型的变量在赋值时默认进行深拷贝(因为值类型的数据直接存储在变量中),而引用类型的变量在赋值时默认进行浅拷贝(因为引用类型的数据存储在堆上,变量中只存储指向数据的指针)。

示例1:引用类型的浅拷贝
package mainimport "fmt"func main() {    slice1 := []int{1, 2, 3}    slice2 := slice1 // 浅拷贝    slice2[0] = 10    fmt.Println(slice1) // 输出: [10 2 3]}

在上面的例子中,slice2slice1的浅拷贝,它们共享同一个底层数组。因此,修改slice2的第一个元素也会影响到slice1

示例2:结构体的浅拷贝
package mainimport "fmt"type Dog struct {    Name string    Age  int}func main() {    dog1 := Dog{Name: "dog1", Age: 11}    dog2 := dog1 // 浅拷贝    dog2.Name = "dog2"    fmt.Println(dog1) // 输出: {dog1 11}    fmt.Println(dog2) // 输出: {dog2 11}}

虽然在这个例子中dog2看起来像是dog1的深拷贝(因为修改dog2.Name没有影响到dog1.Name),但实际上这是因为Dog结构体中的字段都是值类型,它们在赋值时自然进行了深拷贝。如果Dog结构体中包含引用类型的字段,那么就会表现出浅拷贝的特性。

4.2 深拷贝的实现

Go标准库中没有直接提供深拷贝的函数,但可以通过以下几种方式实现深拷贝:

4.2.1 使用json.Marshaljson.Unmarshal

这是一种简单但效率较低的方法,适用于简单的结构体。通过将对象序列化为JSON字符串,然后再反序列化回一个新对象,从而实现深拷贝。

package mainimport (    "encoding/json"    "fmt")type Person struct {    Name string    Age  int}func main() {    person1 := Person{"Alice", 25}    var person2 Person    // 序列化    data, err := json.Marshal(person1)    if err != nil {        panic(err)    }    // 反序列化    err = json.Unmarshal(data, &person2)    if err != nil {        panic(err)    }    person2.Age = 30    fmt.Println(person1) // 输出: {Alice 25}    fmt.Println(person2) // 输出: {Alice 30}}

这种方法虽然简单,但有几个缺点:

需要对象可以序列化为JSON格式,且序列化和反序列化过程需要遍历整个对象,效率较低。如果对象中包含循环引用或特殊类型(如函数、channel等),则无法正确序列化。
4.2.2 递归深拷贝

对于复杂的数据结构,可以使用递归深拷贝的方法。这种方法需要手动编写代码来遍历对象中的所有字段,如果是基本类型则直接复制,如果是复杂类型(如结构体、切片、映射等)则递归调用深拷贝函数。

package mainimport (    "fmt"    "reflect")func DeepCopy(input interface{}) interface{} {    if input == nil {        return nil    }    switch reflect.TypeOf(input).Kind() {    case reflect.Bool, reflect.String, reflect.Int, reflect.Int8, reflect.Int16,        reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64:        return input    case reflect.Struct:        in := reflect.ValueOf(input)        out := reflect.New(in.Type()).Elem()        for i := 0; i < in.NumField(); i++ {            field := in.Field(i)            if field.CanInterface() {                // 递归调用DeepCopy                out.Field(i).Set(reflect.ValueOf(DeepCopy(field.Interface())))            }        }        return out.Interface()    // 可以根据需要添加对切片、映射等类型的支持    default:        // 其他类型根据需要进行处理        return input    }}func main() {    // 示例结构体    type Object struct {        Num   int        Str   string        Slice []int        Map   map[string]int        Person    }    // 匿名结构体Person    type Person struct {        Name string        Age  int    }    obj1 := &Object{        Num:   1,        Str:   "hello",        Slice: []int{2, 3},        Map:   map[string]int{"age": 18},        Person: Person{            Name: "Lucas",            Age:  20,        },    }    // 深拷贝    obj2 := DeepCopy(obj1).(*Object)    // 修改obj1的Name字段    obj1.Person.Name = "Nina"    fmt.Println("obj1:", obj1)    fmt.Println("obj2:", obj2)}// 输出结果:// obj1: &{1 hello [2 3] map[age:18] {Nina 20}}// obj2: &{1 hello [2 3] map[age:18] {Lucas 20}}

在这个例子中,我们定义了一个DeepCopy函数,它使用反射来遍历对象中的所有字段,并根据字段类型进行相应的深拷贝处理。对于结构体类型,我们递归调用DeepCopy函数来处理其内部字段。这样,无论对象包含多少层嵌套,都能实现完整的深拷贝。

五、总结

深拷贝和浅拷贝是Go语言中常见的对象复制方式,它们的主要区别在于是否真正获取到了被拷贝对象的单独掌控权。深拷贝会复制对象的所有数据和子对象,使得新对象与原对象完全独立;而浅拷贝只是复制了对象的引用,使得新对象与原对象共享同一块内存空间。在选择使用深拷贝还是浅拷贝时,需要根据具体的场景和需求来决定。

在实际编程中,深拷贝通常比浅拷贝更加复杂和昂贵,因为它需要递归地复制对象的所有部分,包括嵌套的对象和集合。然而,深拷贝提供了更高的数据独立性和安全性,避免了因修改副本而影响原始数据的风险。


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/173670.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1