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;
}
}

京公网安备 11010502036488号