golang 深拷贝和浅拷贝是什么?
·
一:什么是拷贝?
先想象你有一个盒子(变量),里面装着一个玩具(数据)。拷贝就是复制这个玩具。
-
浅拷贝:只复制盒子本身,但两个盒子里的玩具是同一个
-
深拷贝:连玩具都复制一份全新的,两个盒子里的玩具各自独立
二:浅拷贝(Shallow Copy)
原理
只复制对象的第一层数据。如果对象里有指针、切片、map、通道等引用类型,复制的是引用地址,而不是真实数据。
举例
package main
import "fmt"
func main() {
// 原始切片
original := []int{1, 2, 3}
// 浅拷贝(通过赋值)
shallowCopy := original
// 修改浅拷贝的值
shallowCopy[0] = 999
// 查看结果
fmt.Println("original:", original) // [999 2 3] ← 被影响了!
fmt.Println("shallowCopy:", shallowCopy) // [999 2 3]
}
结果:修改 shallowCopy 后,original 也跟着变了,因为它们底层指向同一个数组。
更多浅拷贝方式
// 方式1:直接赋值
b := a
// 方式2:传递参数(函数传参是浅拷贝)
func test(s []int) { ... }
// 方式3:使用 copy() 对切片部分浅拷贝
copy(dest, src) // 对元素逐个浅拷贝
三:深拷贝(Deep Copy)
原理
递归地复制对象的所有层级,包括引用类型指向的数据。源对象和副本完全独立,互不影响。
举例
package main
import "fmt"
func main() {
// 原始切片
original := []int{1, 2, 3}
// 深拷贝:创建一个新切片并逐个复制元素
deepCopy := make([]int, len(original))
for i, v := range original {
deepCopy[i] = v
}
// 修改深拷贝的值
deepCopy[0] = 999
// 查看结果
fmt.Println("original:", original) // [1 2 3] ← 没变
fmt.Println("deepCopy:", deepCopy) // [999 2 3]
}
对于复杂结构(嵌套切片)
package main
import "fmt"
// 使用递归实现深拷贝(简单示例)
func deepCopy(src [][]int) [][]int {
dst := make([][]int, len(src))
for i, row := range src {
dst[i] = make([]int, len(row))
copy(dst[i], row) // 复制内层切片
}
return dst
}
func main() {
// 嵌套切片
original := [][]int{
{1, 2},
{3, 4},
}
// 浅拷贝
shallow := original
// 深拷贝
deep := deepCopy(original)
// 修改浅拷贝的内层数据
shallow[0][0] = 999
fmt.Println("original:", original) // [[999 2] [3 4]] ← 被影响了
fmt.Println("deep:", deep) // [[1 2] [3 4]] ← 没变
}
四:不同数据类型的拷贝行为
| 数据类型 | 赋值(=)行为 | 说明 |
|---|---|---|
| 基本类型(int, float, bool, string) | 深拷贝 | 值本身直接复制 |
数组 [n]T |
深拷贝 | 数组是值类型 |
切片 []T |
浅拷贝 | 复制的是指针、长度、容量 |
| map | 浅拷贝 | 复制的是底层指针 |
指针 *T |
浅拷贝 | 复制的是内存地址 |
| 结构体 struct | 看字段类型 | 字段如果是引用类型则浅拷贝 |
// 数组示例(值类型,自动深拷贝)
arr1 := [3]int{1, 2, 3}
arr2 := arr1
arr2[0] = 999
fmt.Println(arr1[0]) // 1 ← 没变(深拷贝)
五:如何实现真正的深拷贝?
方法1:手动复制(简单结构)
// 复制切片
newSlice := make([]int, len(oldSlice))
copy(newSlice, oldSlice)
// 复制map
newMap := make(map[string]int)
for k, v := range oldMap {
newMap[k] = v
}
方法2:使用 gob 编码(通用方法)
import (
"bytes"
"encoding/gob"
)
func DeepCopy(src, dst interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(&buf).Decode(dst)
}
方法3:使用 json 序列化(简单但效率低)
func DeepCopy(src, dst interface{}) error {
data, err := json.Marshal(src)
if err != nil {
return err
}
return json.Unmarshal(data, dst)
}
六:实际开发建议
-
默认使用浅拷贝:Go 语言设计倾向值传递(基本类型)和引用传递(切片、map),大多数情况够用
-
需要深拷贝的场景:
-
需要独立修改副本,不影响原数据
-
并发场景中避免数据竞争
-
保存快照、撤销操作等
-
-
注意:不要自己写复杂递归深拷贝,容易出错。使用成熟的库如
github.com/mohae/deepcopy
七:总结一句话
浅拷贝 = 复制遥控器(指向同一个电视机)
深拷贝 = 买一台新电视机(完全独立的设备)
更多推荐

所有评论(0)