谈谈浅复制和深复制

   复制分浅复制和深复制两种 一般是用于描述数组或对象有嵌套结构的数据的复制

    注意复制得到的数据应该是要与原数据是没有关系的,即一方数据改变了,另一方的数据不会随之改变

    浅复制是指只复制了最外层的数据

    深复制是指把整个数据都复制了

    基础类型的数据可以直接赋值复制,因为它们没有引用,复制得到的数据和原数据没有任何关系

    那如果复制一个二维数组呢?此时如果用浅复制的方式的话,那么得到的复制的数据并不是完全和原数据脱离关系

    为什么说是没有完全脱离呢?因为如果数组的子元素不是基础数据类型时(如数组,对象),复制所得的元素复制的不是它的值,而是它的引用地址。所以此时原数据的这个子元素和复制所得元素是指向同一片数据的,当其任意一方里的子元素有改动时,另一方的子元素也会随之被改变。

    如下面的例子:

      arr = [1, [2, 3, 4], 5];
      var arr1 = [];
      for (var prop in arr) {
        arr1[prop] = arr[prop];
      }
      arr1[1][2] = 1000;
      console.log("arr[1][2]=" + arr[1][2]);
      console.log("arr1===arr?" + (arr1 === arr));
      console.log("arr1[1]===arr[1?" + (arr1[1] === arr[1]));
      console.log(arr1, arr);

复制所得数据的子元素arr[1]和原数据的子元素arr[1]是指向同一片数据的,当复制所得子元素arr[1]的子元素arr1[1][2]的值改为1000时,arr[1][2]的值也为1000了。

    通过打印的结果可以知道arr与arr1并不是同一个东西,但是通过浅复制方法得到的数据arr中的嵌套子元素arr[1]和arr[1]是同一个东西

 

    既然问题在于子元素是嵌套结构时元素无法直接复制到它的值,那么我们就用递归来一层一层的复制

    递归解决的思想是:

    当这个数据的子元素都是基本数据类型时,直接用for in复制这个对象数据类型的数据

    否则就是有嵌套结构,此时把嵌套的部分再拿出来复制,直至最里面的一层为止

递归实现深递归:

var obj1 = {
        a: 1,
        d: 1,
        b: {
          a: 2,
          d: 2,
          b: {
            a: 3,
            d: 3,
            b: {
              a: 4,
              d: 4,
              b: {
                a: 5
              },
              c: 4
            },
            C: {
              a: 5,
              d: 5,
              b: {
                a: 6
              },
              c: 6
            }
          },
          c: 2
        },
        c: 1
      };
      var arr1 = [
        1,
        2,
        3,
        [
          5,
          6,
          7,
          {
            a: 5,
            d: 5,
            b: {
              a: 6
            },
            c: 6
          },
          10
        ],
        11
      ];
      // 调用初始函数 并将返回值赋给变量
      var obj = init(obj1);
      var arr = init(arr1);
      // 定义初始函数 函数参数为obj1 用于接收需要复制的数据 返回复制好的数据
      function init(obj1) {
        // 判断这个数据是数组还是对象,分别定义不同类型的复制类型 obj2为复制好的数据
        if (obj1.constructor === Array) {
          var obj2 = [];
        } else {
          var obj2 = {};
        }
        // 调用复制函数 参数为需要处理的数据 复制好的数据 没有返回值
        copy(obj1, obj2);
        return obj2;
      }
      // 定义复制函数
      function copy(obj3, obj4) {
        // 将需要处理的数据通过for in循环 判断当前所需处理数据的子元素是否有嵌套,如果没有直接复制 有的话需要
        // 把当前的属性赋给当前正在复制的元素,然后判断属性值是什么类型的数据,并赋给它对应的空对象(空数组或
        // 空对象) 然后递归复制函数
        for (var prop in obj3) {
          if (
            obj3[prop].constructor !== Array &&
            obj3[prop].constructor !== Object
          ) {
            obj4[prop] = obj3[prop];
          } else {
            if (obj3[prop].constructor === Array) {
              obj4[prop] = [];
            } else {
              obj4[prop] = {};
            }
            copy(obj3[prop], obj4[prop]);
          }
        }
      }
      console.log("obj === obj1?"+(obj === obj1));
      console.log("arr === arr1?"+(arr === arr1));
      console.log("obj.b === obj1.b?"+(obj.b === obj1.b));
      console.log(obj1,obj);
      console.log(arr1,arr);

执行结果:

 

### 深拷贝与浅拷贝的概念 在 Java 中,深拷贝浅拷贝是用来描述对象复制行为的术语。浅拷贝是指创建一个新的对象,但是这个新对象中的引用类型字段仍然指向原始对象中的相同数据[^1]。这意味着如果修改了这些引用所指向的数据,则两个对象都会受到影响。 相比之下,深拷贝不仅会创建新的对象实例,还会递归地复制该对象内部所有的引用类型成员变量及其依赖的对象树结构,从而确保源对象与其副本之间没有任何关联[^2]。这样做的好处在于更改其中一个对象的内容不会影响到另一个对象的状态。 --- ### 浅拷贝的特点及实现方式 #### 特点 当执行浅拷贝操作时,基础数据类型会被直接赋值给目标对象;而对于那些属于引用类型的属性来说,则仅仅只是把原来的地址传递过去而已——也就是说两者依旧共同拥有同一个实际存在的实体部分[^3]。 #### 实现方法之一:`Object.clone()` 方法 要完成这种形式下的克隆动作可以通过继承自 `java.lang.Object` 类里的默认无参构造器或者覆盖其定义好的 protected 函数 clone 来达成目的。需要注意的是类必须实现 Cloneable 接口以避免抛出异常,并且父级类别也需要支持同样的功能以便能够正常运作[^1]。 ```java public class ShallowCopyExample implements Cloneable { private int value; private String reference; public Object clone() throws CloneNotSupportedException { return super.clone(); // 使用默认的浅拷贝机制 } } ``` --- ### 深拷贝的特点及实现方式 #### 特点 正如前面提到过的那样,在做深层次上的备份过程中除了简单数值之外还需要考虑如何处理复杂嵌套关系以及动态分配出来的额外空间等问题。最终目的是让每一个组成部分都成为独一无二的存在而不与其他任何地方发生联系[^2]。 #### 实现方法之一:利用序列化(Serializable) 一种常见的做法就是借助于 Java 提供的标准库特性 Serializable 将整个体系转化为字节数组后再反向解析回来形成全新的个体。这种方法虽然效率较低但对于大多数情况而言已经足够用了[^3]。 ```java import java.io.*; public class DeepCopyExample implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private transient int[] array; // 需要注意标记为transient的字段不会被自动序列化 @Override public Object clone() throws IOException, ClassNotFoundException{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (DeepCopyExample)ois.readObject(); } } ``` 另一种可能的选择是手动编写相应的辅助函数逐一访问各个子节点逐层构建起完整的映射图谱直至达到最底层为止然后再按照相反顺序依次组装回去得到期望的结果集。不过这种方式通常较为繁琐而且容易出错所以除非特殊需求否则一般不推荐采用此策略[^1]。 --- ### 总结对比表 | **比较维度** | **浅拷贝** | **深拷贝** | |--------------------|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | **性能开销** | 较低 | 更高,尤其是面对大型或深层嵌套的对象 | | **安全性/独立性** | 新旧两份资料间存在潜在耦合风险 | 完全隔离,互不影响 | | **适用范围** | 当不需要担心后续变更传播至其他位置时可选用 | 如果希望获得绝对意义上的全新版本则应优先考虑 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值