一:什么是拷贝?

先想象你有一个盒子(变量),里面装着一个玩具(数据)。拷贝就是复制这个玩具。

  • 浅拷贝:只复制盒子本身,但两个盒子里的玩具是同一个

  • 深拷贝:连玩具都复制一份全新的,两个盒子里的玩具各自独立

二:浅拷贝(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)
}

六:实际开发建议

  1. 默认使用浅拷贝:Go 语言设计倾向值传递(基本类型)和引用传递(切片、map),大多数情况够用

  2. 需要深拷贝的场景

    • 需要独立修改副本,不影响原数据

    • 并发场景中避免数据竞争

    • 保存快照、撤销操作等

  3. 注意:不要自己写复杂递归深拷贝,容易出错。使用成熟的库如 github.com/mohae/deepcopy

七:总结一句话

浅拷贝 = 复制遥控器(指向同一个电视机)
深拷贝 = 买一台新电视机(完全独立的设备)

更多推荐