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

深入探究编程拷贝

起因

        被一个问题问住了,JAVA里的“指针拷贝”,我的第一印象是JAVA是没有指针的?是不是引用拷贝。趁此机会,干脆总结一下各种拷贝把。

1. 浅拷贝(Shallow Copy)

  • 定义:只复制对象的最外层,如果对象包含嵌套对象(如数组、字典、引用类型等),则嵌套对象仍然是共享的(拷贝的是引用,而非实际数据)。

  • 特点

    • 基本类型(如数字、字符串)会完全复制。

    • 引用类型(如对象、数组)只会复制引用地址,新旧对象共享同一块内存。

  • 问题:修改拷贝后的对象中的嵌套属性时,原始对象也可能被修改。

  • 示例

    • JavaScript 的 Object.assign() 或扩展运算符 {...obj}

    • Python 的 copy.copy()

  • // JavaScript 示例
    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = { ...original };
    shallowCopy.b.c = 99; // 修改嵌套属性
    console.log(original.b.c); // 输出 99(原始对象也被修改)

2. 深拷贝(Deep Copy)

  • 定义:完全复制对象及其所有嵌套对象,新旧对象完全独立,不共享任何内存。

  • 特点

    • 所有层级的数据都会被复制一份。

    • 修改拷贝后的对象不会影响原始对象。

  • 缺点:性能开销较大(尤其是嵌套层级深或数据量大时)。

  • 示例

    • JavaScript 的 JSON.parse(JSON.stringify(obj))(局限性:无法处理函数、undefined 等)。

    • Python 的 copy.deepcopy()

    • Lodash 的 _.cloneDeep()

    • // JavaScript 示例
      const original = { a: 1, b: { c: 2 } };
      const deepCopy = JSON.parse(JSON.stringify(original));
      deepCopy.b.c = 99;
      console.log(original.b.c); // 输出 2(原始对象不受影响)

 

3. 指针拷贝(引用拷贝)

  • 定义:直接复制对象的引用(内存地址),新旧变量指向同一块内存。

  • 特点

    • 没有创建新对象,只是多了一个指向原对象的指针。

    • 修改任意变量都会影响其他变量。

  • 示例

    • 大多数语言中直接赋值的行为(如 JavaScript 的对象赋值、Python 的列表赋值)。

// JavaScript 示例
const original = { a: 1 };
const pointerCopy = original; // 指针拷贝
pointerCopy.a = 2;
console.log(original.a); // 输出 2(原始对象被修改)

 

对比总结

拷贝方式复制内容嵌套对象是否独立性能典型场景
指针拷贝只复制引用(内存地址)❌ 共享最高需要引用同一对象时
浅拷贝复制外层,嵌套对象共享引用❌ 嵌套共享中等简单对象的快速复制
深拷贝递归复制所有层级✅ 完全独立最低需要完全独立的对象副本时

4. 写时拷贝(Copy-on-Write, COW)


8. 零拷贝(Zero-Copy)


9. 混合拷贝(Hybrid Copy)


注意事项

根据需求选择合适的拷贝方式:优先使用浅拷贝(性能高),必要时再用深拷贝(安全性高)。

  • 定义:一种优化技术,只有在数据被修改时才会真正执行拷贝,否则共享原始数据。

  • 特点

    • 初始时只是引用拷贝(类似指针拷贝),节省内存和计算资源。

    • 当尝试修改数据时,系统自动创建副本,确保原始数据不变。

  • 应用场景

    • 文件系统(如 Linux 的 fork() 子进程共享父进程内存,直到写入时才拷贝)。

    • 某些编程语言的数据结构(如 Swift 的 Array、Rust 的 Cow 类型)。

    • 数据库的快照隔离(Snapshot Isolation)。

  • // Rust 的 Cow(Copy-on-Write)示例
    use std::borrow::Cow;fn process_data(data: Cow<str>) {// 如果 data 是引用,且未被修改,则不会触发拷贝println!("Data: {}", data);
    }let static_str = "hello"; // 静态字符串(不可变)
    let owned_str = String::from("world"); // 可变字符串process_data(Cow::Borrowed(static_str)); // 不拷贝
    process_data(Cow::Owned(owned_str)); // 已拥有,无需拷贝

    5. 延迟拷贝(Lazy Copy)

  • 类似 COW,但更通用

    • 不一定是“写时”才拷贝,可能是按需计算或缓存结果后再拷贝。

    • 常见于函数式编程(如 Haskell 的惰性求值)。

  • 6. 结构化拷贝(Structured Clone)

  • 定义:一种比 JSON.parse(JSON.stringify()) 更强大的深拷贝方式,能处理循环引用、BlobDate 等特殊对象。

  • 特点

    • 浏览器环境支持 structuredClone() API。

    • Node.js 也有类似的实现(如 v8.deserialize(v8.serialize(obj)))。

  • 示例

  • const original = { a: 1, date: new Date(), nested: { b: 2 } };
    original.self = original; // 循环引用const cloned = structuredClone(original); // 正确处理循环引用和 Date
    console.log(cloned.date instanceof Date); // true

    7. 影子拷贝(Shadow Copy)

  • 定义:部分拷贝,仅复制对象的某些属性(如元数据),而非全部数据。

  • 应用场景

    • 数据库的“影子表”(Shadow Tables)。

    • 虚拟机的快照(仅记录变化部分)。

  • 定义:不复制数据,而是直接共享底层内存(如内存映射文件、DMA 传输)。

  • 特点

    • 最高性能,适合大数据传输(如网络协议、GPU 计算)。

    • 需要底层支持(如 Linux 的 sendfile()mmap())。

  • 定义:结合不同拷贝策略,如:

    • 浅拷贝 + 按需深拷贝(类似 COW)。

    • 部分深拷贝(如只复制某些嵌套层级)。

    • 示例

      • Node.js 的 stream.pipe()(零拷贝文件传输)。

      • Rust 的 Bytes 类型(引用计数共享内存)。

      对比总结

      拷贝方式是否共享数据触发条件典型应用场景
      指针拷贝✅ 共享直接赋值引用传递、性能优化
      浅拷贝❌(仅外层)显式调用简单对象复制
      深拷贝❌ 完全独立显式调用需要完全隔离的数据
      写时拷贝✅(初始)首次修改时高性能共享(如 fork()
      结构化拷贝❌ 完全独立显式调用浏览器环境深拷贝
      零拷贝✅ 共享底层优化大数据传输(如 mmap
      影子拷贝部分共享按需复制数据库快照、虚拟机

      如何选择合适的拷贝方式?

      • 默认用指针拷贝(如果不需要独立数据)。

      • 需要独立数据但性能敏感 → 浅拷贝或写时拷贝(COW)。

      • 需要完全隔离 → 深拷贝或结构化拷贝。

      • 大数据传输 → 零拷贝技术(如 mmapsendfile)。

      • 特殊需求(如循环引用)→ structuredClone 或自定义深拷贝。

    1. 循环引用:深拷贝时如果对象存在循环引用(如 a.b = a),普通方法会报栈溢出,需特殊处理。

    2. 特殊类型:函数、SymbolSet/Map 等可能需要额外处理。

    3. 语言差异:不同语言对浅拷贝/深拷贝的实现可能不同(如 Python 的 list.copy() 是浅拷贝)。

http://www.dtcms.com/a/274793.html

相关文章:

  • mysql的性能优化:组提交、数据页复用、全表扫描优化、刷脏页
  • Vue 表单开发避坑指南:从响应式数据到动态规则的实践总结
  • Go 编译报错排查:vendor/golang.org/x/crypto/cryptobyte/asn1 no Go source files
  • Java外包怎么选?这几点不注意,项目可能血亏!
  • day21——特殊文件:XML、Properties、以及日志框架
  • Linux中geoserver中文乱码
  • 离线环境二进制安装docker
  • uniapp获取状态栏高度,胶囊按钮的高度,底部安全区域的高度,自定义导航栏
  • [实战]调频三角波和锯齿波信号生成(完整C代码)
  • hbuilderx打包的应用上传苹果应用商店最简方法
  • 字节豆包又一个新功能,超级实用,4 种玩法,你肯定用得上!(建议收藏)
  • Uniapp视频聊天软件内容监控插件开发指南
  • OA系统中的搜索功能方案:简单搜索vs高级搜索
  • 2-Git提交本地项目到远程仓库
  • 问有几条病狗?
  • 【linux网络】深入理解 TCP/UDP:从基础端口号到可靠传输机制全解析
  • 机器学习-06(Optimization-自动调整学习率)
  • consul 的安装与服务发现
  • MOSS-TTSD V2版 - 文本到语音对话生成 支持零样本多人语音克隆 一键整合包下载
  • 一文速览DeepSeek-R1的本地部署——可联网、可实现本地知识库问答(附教程)
  • OBB旋转框检测配置与训练全流程(基于 DOTA8 数据集)
  • 第3章 操作臂运动学(笔记总结)
  • Hangfire 调用报错解决方案总结
  • 经典的垃圾收集器!!!
  • day02-数组part02
  • day67—DFS—被围绕的区域(LeetCode-130)
  • 飞算JavaAI 实战笔记
  • Qt中QGraphicsView类应用解析:构建高效2D图形界面的核心技术
  • 迭代器(c++)、智能指针
  • 【C/C++】动态内存分配:从 C++98 裸指针到现代策略