当前位置: 首页 > news >正文

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

在这里插入图片描述

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

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

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,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] = 10fmt.Println(slice1) // 输出: [10 2 3]
}

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

示例2:结构体的浅拷贝
package mainimport "fmt"type Dog struct {Name stringAge  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 stringAge  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 = 30fmt.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 inputcase 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() {// 递归调用DeepCopyout.Field(i).Set(reflect.ValueOf(DeepCopy(field.Interface())))}}return out.Interface()// 可以根据需要添加对切片、映射等类型的支持default:// 其他类型根据需要进行处理return input}
}func main() {// 示例结构体type Object struct {Num   intStr   stringSlice []intMap   map[string]intPerson}// 匿名结构体Persontype Person struct {Name stringAge  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://www.mrgr.cn/news/34340.html

相关文章:

  • Java详细学习路线:从入门到精通的全方位指南
  • 数字人起飞!字节Loopy对口型功能上线 可根据语境匹配表情和情绪
  • 一个可以在线制作样本册,拥有海量样本图册模板可以套用的网站
  • Vert.x,Core - Future
  • 视频无损压缩工具+预览视频生成工具
  • Java 中使用 Gson 实现深度克隆 #什么是深克隆与浅克隆?#clone方法为什么不能直接通过某个对象实例在外部类调用?
  • 我设置了路由器自动切换ip,这会让我的账号登录地址经常改变吗
  • 奔驰「进退」两难
  • Webpack 常见配置项
  • apply、call和bind的作用和区别
  • 装饰器模式
  • Vue使用Vue Router路由:开发单页应用
  • 【网络协议栈】传输层的意义 和 UDP协议结构的解析(内含逻辑图解通俗易懂)
  • yolo自动化项目实例解析(四)ui页面整理1 (1.85)
  • kafka负载均衡迁移(通过kafka eagle)
  • 独立站崛起:2024全球商家共谋增长新蓝图
  • Kibana中突然看不到日志ElasticSearch突然采集不到日志问题解决分析
  • Linux——虚拟机和Windows间的文件传输方式
  • 【运维监控】influxdb 2.0 + grafana 11 监控jmeter 5.6.3 性能指标(1)
  • 9.23-部署项目