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

news2024/9/23 18:47:17

在这里插入图片描述

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

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

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,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 main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3}
    slice2 := slice1 // 浅拷贝
    slice2[0] = 10
    fmt.Println(slice1) // 输出: [10 2 3]
}

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

示例2:结构体的浅拷贝
package main

import "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 main

import (
    "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 main

import (
    "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://www.coloradmin.cn/o/2158220.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows自定义路径docker安装

环境: win11(win10也是同样流程) docker:Docker version 27.1.1, build 6312585 目录 1.下载docker 2.自定义路径安装 3.修改镜像保存路径 非win系统的同学可以参考&#xff1a;官方说明文档 1.下载docker docker官网下载链接 进入官网后选择对应的系统版本下载即可。 …

IDEA明明模块和环境变量配置的是JDK8但是显示别的版本解决方案

IDEA明明模块和环境变量配置的是JDK8但是显示别的版本解决方案 我目前系统中存在JDK8&#xff0c;JDK11,JDK17等多个版本&#xff0c;方便开发使用&#xff0c;但是有些时候也是比较烦人的&#xff0c;因为不同版本的JDK包有所区别。 需要注意的几个地方。方便自己排查一下。

Vue3:具名插槽

目录 一.性质与作用 1.基本性质 2.使用方式 3.作用 4.应用场景 5.注意事项 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 具名插槽在Vue3中用于为组件提供一种方式&#xff0c;允许父组件向子组件注入内容&#xff0c;并且可以指定这些内…

硬盘数据能否自己在家恢复?探索数据恢复的可行性与方法

随着数字化时代的到来&#xff0c;硬盘作为我们存储大量重要数据的设备&#xff0c;其重要性不言而喻。然而&#xff0c;当硬盘出现问题&#xff0c;如误删除、硬盘损坏、病毒感染等&#xff0c;导致数据丢失时&#xff0c;我们往往会感到十分焦虑。那么&#xff0c;硬盘数据能…

阿里巴巴新推出Java版AI 应用开发框架-Spring AI Alibaba

[Spring AI Alibaba 是一款 Java 语言实现的 AI 应用开发框架&#xff0c;旨在简化 Java AI 应用程序开发&#xff0c;让 Java 开发者像使用 Spring 开发普通应用一样开发 AI 应用。Spring AI Alibaba 基于 Spring AI 开源项目构建&#xff0c;默认提供阿里云基础模型服务、开源…

【BetterBench博士】2024年华为杯E题:高速公路应急车道紧急启用模型 Python代码实现

题目 【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析 【BetterBench博士】2024年中国研究生数学建模竞赛 E题&#xff1a;高速公路应急车道紧急启用模型 问题分析 【BetterBench博士】2024年中国研究生数学建模竞赛 C题&#xff1a;数据驱动…

杰发科技——Eclipse环境安装

文件已传到网盘&#xff1a; 1. 安装文件准备 2. 安装Make 默认路径&#xff1a;C:\Program Files (x86)\GnuWin32\bin\ 不复制的话会报错 Error: Program "make" not found in PATH 3. 安装工具链 默认路径&#xff1a;C:\Program Files (x86)\Arm GNU Toolchain…

阅读CVPR论文——mPLUG-Owl2:革命性的多模态大语言模型与模态协作

读后感悟&#xff1a; 1&#xff09;实验部分非常丰富&#xff0c;并且论文中的图制作的非常精美&#xff0c;论文开篇的图制作的别出心裁&#xff0c;将几种不同的方法表现出的性能差异不是以普通的表格形式展现&#xff0c;而是制作成了一副环状折线图&#xff0c;论文中其他…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-19

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-19 1. SAM4MLLM: Enhance Multi-Modal Large Language Model for Referring Expression Segmentation Authors: Yi-Chia Chen, Wei-Hua Li, Cheng Sun, Yu-Chiang Frank Wang, Chu-Song Chen SAM4MLLM: 增强多模…

网络通信——NAT(网络地址转换)

一.NAT是什么 1.首先运营商维护的网络时公网的&#xff0c;使用的是公网的IP地址&#xff0c;而私有的IP地址的不能的在公网上路由&#xff0c;为了保证网络互通&#xff0c;所以有了NAT技术。 2.NAT一般部署在公网和私网上的网关设备上面。 二.为什么会这样 因为你只要出到…

CAD图纸防泄密如何实现?六个措施杜绝泄密风险!

在工程设计领域&#xff0c;CAD图纸作为企业的核心机密之一&#xff0c;其安全性直接关系到企业的技术优势和商业竞争力。 然而&#xff0c;随着信息技术的不断发展&#xff0c;CAD图纸的泄密风险也日益增加。 为了确保CAD图纸的安全&#xff0c;企业必须采取一系列有效的防泄…

ubuntu下使用qt编译QOCI(libqsqloci.so)驱动详解及测试

1、系统及软件版本:ubuntu16.04LTS 安装在WMare虚拟机上,qt的版本是QT5.12.11 2、下载编译libqsqloci.so驱动所需的oracle相关sdk包和basic包,我下载的是11.2.0.4版本,下载地址:https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.htm…

(done) 声音信号处理基础知识(1) (course overview)

来源&#xff1a;https://www.youtube.com/watch?viCwMQJnKk2c 声学处理应用场景如下 这个系列的内容包括如下&#xff1a; 作者的 slack 频道 油管主的 github repo: https://github.com/musikalkemist/AudioSignalProcessingForML

GRE隧道协议学习笔记

使用场景 分布在不同地理位置的总公司和分公司怎么通过网络连接起来&#xff1f; 可以使用ISP网络连接。在豆包中可以看到如下回答通俗的讲就是运营商收费提供网络服务&#xff0c;有个人的有企业的&#xff0c;企业的很贵 为什么要使用GRE隧道 当然你也可以用其他隧道协议…

使用PyTorch检测和验证多GPU环境的Python脚本

使用PyTorch检测和验证多GPU环境的Python脚本 在深度学习和机器学习中&#xff0c;GPU的计算能力对模型训练和推理的速度有着极大的影响。随着多GPU系统的普及&#xff0c;如何确保多GPU能被正确识别并使用&#xff0c;是一个非常关键的问题。本文将为大家介绍一段简洁的Pytho…

新一代图像生成E2E FT:深度图微调突破

文章地址&#xff1a;Fine-Tuning Image-Conditional Diffusion Models is Easier than You Think 项目主页&#xff1a;https://gonzalomartingarcia.github.io/diffusion-e2e-ft/ 代码地址&#xff1a;https://github.com/VisualComputingInstitute/diffusion-e2e-ft 机构&am…

【常见框架漏洞】ThinkPHP、struts2、Spring、Shiro

一、ThinkPHP 1.环境配置 靶场:vulhub/thinkphp/5-rcedocker-compose up -d #启动环境 访问靶场:http://ip:8080/index.php2.远程命令执行 执行whoami命令 poc: http://47.121.211.205:8080/index.php?sindex/think\app/invokefunction&functioncall_user_func_array&…

物联网(IoT)中基于深度学习的入侵检测系统的综合综述

这篇论文是一篇全面的综述&#xff0c;标题为“A comprehensive survey on deep learning-based intrusion detection systems in Internet of Things (IoT)”&#xff0c;作者是Qasem Abu Al-Haija和Ayat Droos。论文主要探讨了在物联网(IoT)环境中基于深度学习的入侵检测系统…

Python自动化-操作Excel

在数据处理和报表生成过程中&#xff0c;Excel是一个经常使用的工具。Python中的openpyxl库可以让您通过编程方式读取、写入和操作Excel文件&#xff0c;从而实现自动化的数据处理和报表生成。本文将介绍openpyxl库的基本用法和常见操作&#xff0c;帮助您快速上手使用Python处…

蘑菇成熟待收检测系统源码分享

蘑菇成熟待收检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…