✨✨ 欢迎大家来到景天科技苑✨✨
?? 养成好习惯,先赞后看哦~??
? 作者简介:景天科技苑
?《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,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]}
在上面的例子中,slice2
是slice1
的浅拷贝,它们共享同一个底层数组。因此,修改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.Marshal
和json.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语言中常见的对象复制方式,它们的主要区别在于是否真正获取到了被拷贝对象的单独掌控权。深拷贝会复制对象的所有数据和子对象,使得新对象与原对象完全独立;而浅拷贝只是复制了对象的引用,使得新对象与原对象共享同一块内存空间。在选择使用深拷贝还是浅拷贝时,需要根据具体的场景和需求来决定。
在实际编程中,深拷贝通常比浅拷贝更加复杂和昂贵,因为它需要递归地复制对象的所有部分,包括嵌套的对象和集合。然而,深拷贝提供了更高的数据独立性和安全性,避免了因修改副本而影响原始数据的风险。