对象的深拷贝与浅拷贝

JVM中对象复制的机制

一、浅复制:只对对象及变量值进行复制,如果这个对象中引用了其他对象的话,这个对象被复制以后,它引用对象的地址不变。(两个完全相同的对象会指向同一个引用对象)

在Object中,存在一个对象克隆的方法,这里的复制是浅复制

        Dancer d1 = new Dancer();
        d1.setName("Joey");
        Dancer d2 = new Dancer();
        d2.setName("Lily");
        d1.setPartner(d2);
        //打印原始d1对象中的引用的d2对象的 Hash Code
        System.out.println("Partner:" + d2.hashCode());

        //浅复制:
        Dancer shallow = (Dancer) d1.clone();
        System.out.println("浅复制:" + shallow.getPartner().hashCode());

结果:
两个对象的引用对象的地址相同

Partner:2083562754
浅复制:2083562754

二、深复制:不仅对象及变量值进行复制,引用对象也进行复制。(这两个对象都有独立的引用对象,彼此互不干扰)

1)深复制的实现是基于序列化的方式完成的,因此想要实现深复制的对象,对象类应实现Cloneable和Serializable两个接口。

2)涉及到的类:ByteArrayOutputStream字节数组输出流、ObjectOutputStream对象输出流、ByteArrayInputStream字节数组输入流、ObjectInputStream对象输入流

/**
 * Cloneable 说明当前类的对象可以被克隆
 * Serializable 可序列化接口,支撑深复制
 */
public class Dancer implements Cloneable,Serializable{
    private String name;
    private Dancer partner;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dancer getPartner() {
        return partner;
    }

    public void setPartner(Dancer partner) {
        this.partner = partner;
    }

    /**
     * 深复制是基于序列化的方式完成
     * @return 深复制的对象
     */
    public Dancer deepClone() throws IOException, ClassNotFoundException {
        //1.将内存中的对象输出为字节数组,bos对象保存了当前对象的字节数组
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //2.将字节数组反序列化,还原成对象,完成深复制的任务
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Dancer)ois.readObject();
    }
}

调用方法,完成对象的深复制:

        Dancer d1 = new Dancer();
        d1.setName("Joey");
        Dancer d2 = new Dancer();
        d2.setName("Lily");
        d1.setPartner(d2);
        //打印原始d1对象中的引用的d2对象的 Hash Code
        System.out.println("Partner:" + d2.hashCode());

        //浅复制:
        Dancer shallow = (Dancer) d1.clone();
        System.out.println("浅复制:" + shallow.getPartner().hashCode());

        //深复制:将引用对象一并复制
        Dancer deep = (Dancer) d1.deepClone();
        System.out.println("深复制:" + deep.getPartner().hashCode());

结果:两个对象的引用对象的地址不同

Partner:2083562754
浅复制:2083562754
深复制:853119666

LeetCode 138复制带随机指针的链表

题目

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为null无序列表内容
    你的代码接受原链表的头节点 head 作为传入参数。
    图片说明
    图片说明

    思路一:

第一步:创建新节点放在原节点后面,形成2倍长的大链表
第二步:设置新节点的随机节点
第三步:将新链表拆除出来

class Solution {
    public Node copyRandomList(Node head) {
        if(head==null) {
            return null;
        }
        Node p = head;
        //第一步,在每个原节点后面创建一个新节点
        //1->1'->2->2'->3->3'
        while(p!=null) {
            Node newNode = new Node(p.val);
            newNode.next = p.next;
            p.next = newNode;
            p = newNode.next;
        }
        p = head;
        //第二步,设置新节点的随机节点
        while(p!=null) {
            if(p.random!=null) {
                p.next.random = p.random.next;
            }
            p = p.next.next;
        }
        Node dummy = new Node(-1);
        p = head;
        Node cur = dummy;
        //第三步,将两个链表分离
        while(p!=null) {
            cur.next = p.next;
            cur = cur.next;
            p.next = cur.next;
            p = p.next;
        }
        return dummy.next;
    }
}
全部评论

相关推荐

10-07 23:57
已编辑
电子科技大学 Java
八街九陌:博士?客户端?开发?啊?
点赞 评论 收藏
分享
11-09 11:01
济南大学 Java
Java抽象带篮子:外卖项目真得美化一下,可以看看我的详细的外卖话术帖子
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务