使用循环引用深度复制Java对象
如何为使用循环引用深度复制Java对象,java,Java,如何为Foo实现深度拷贝?它包含一个Bar的实例,然后它引用了该Foo public class Foo { Bar bar; Foo () { bar = new Bar(this); } Foo (Foo oldFoo) { bar = new Bar(oldFoo.bar); } public static void main(String[] args) { Foo foo = new
Foo
实现深度拷贝?它包含一个Bar
的实例,然后它引用了该Foo
public class Foo {
Bar bar;
Foo () {
bar = new Bar(this);
}
Foo (Foo oldFoo) {
bar = new Bar(oldFoo.bar);
}
public static void main(String[] args) {
Foo foo = new Foo();
Foo newFoo = new Foo(foo);
}
class Bar {
Foo foo;
Bar (Foo foo) {
this.foo = foo;
}
Bar (Bar oldBar) {
foo = newFoo(oldbar.Foo);
}
}
}
目前,由于无限递归,此代码将导致堆栈溢出
而且,这是我能构造的最简单的例子。实际上,对象图会更大,有多个实例变量,它们本身可能是集合。想想多个Bar
s,比如多个Foo
s
编辑:我目前正在实施@chiatic security的方法。我为福做的对吗?我使用一个单独的HashMap来包含对象图的所有部分,这样我就可以尽可能地编写深度复制功能
Foo (Foo oldFoo) throws Exception {
this(oldFoo, new IdentityHashMap<Object, Object>(), new IdentityHashSet<Object>());
}
Foo (Foo oldFoo, IdentityHashMap<Object, Object> clonedObjects, IdentityHashSet<Object> cloning) throws Exception {
System.out.println("Copying a Foo");
HashMap<Object, Object> newToOldObjectGraph = new HashMap<Object, Object>();
newToOldObjectGraph.put(bar, oldFoo.bar);
deepCopy(newToOldObjectGraph, clonedObjects, cloning);
}
void deepCopy(HashMap<Object, Object> newToOldObjectGraph, IdentityHashMap<Object, Object> clonedObjects, IdentityHashSet<Object> cloning) throws Exception {
for (Entry<Object, Object> entry : newToOldObjectGraph.entrySet()) {
Object newObj = entry.getKey();
Object oldObj = entry.getValue();
if (clonedObjects.containsKey(oldObj)) {
newObj = clonedObjects.get(oldObj);
}
else if (cloning.contains(oldObj)){
newObj = null;
}
else {
cloning.add(oldObj);
// Recursively deep clone
newObj = newObj.getClass().getConstructor(oldObj.getClass(), clonedObjects.getClass(), cloning.getClass()).
newInstance(oldObj, clonedObjects, cloning);
clonedObjects.put(oldObj, newObj);
cloning.remove(oldObj);
}
if (newObj == null && clonedObjects.containsKey(oldObj)) {
newObj = clonedObjects.get(oldObj);
}
}
}
Foo(Foo oldFoo)引发异常{
这(oldFoo,newidentityhashmap(),newidentityhashset());
}
Foo(Foo oldFoo,IdentityHashMap clonedObjects,IdentityHashSet cloning)引发异常{
System.out.println(“复制一个Foo”);
HashMap newToOldObjectGraph=新HashMap();
newToOldObjectGraph.put(bar,oldFoo.bar);
deepCopy(NewToolObjectGraph、clonedObjects、克隆);
}
void deepCopy(HashMap newToOldObjectGraph、IdentityHashMap clonedObjects、IdentityHashSet cloning)引发异常{
for(条目:newToOldObjectGraph.entrySet()){
Object newObj=entry.getKey();
Object oldObj=entry.getValue();
if(clonedObjects.containsKey(oldObj)){
newObj=clonedObjects.get(oldObj);
}
else if(cloning.contains(oldObj)){
newObj=null;
}
否则{
克隆.add(oldObj);
//递归深度克隆
newObj=newObj.getClass().getConstructor(oldObj.getClass(),clonedObjects.getClass(),cloning.getClass())。
newInstance(oldObj、clonedObjects、克隆);
clonedObjects.put(oldObj,newObj);
克隆。删除(oldObj);
}
if(newObj==null&&clonedObjects.containsKey(oldObj)){
newObj=clonedObjects.get(oldObj);
}
}
}
实现可能涉及循环引用的深度副本的最简单方法是使用IdentityHashMap
和IdentityHashSet
(来自)。当您要复制时:
IdentityHashMap
,将源对象映射到它们的克隆IdentityHashSet
,以跟踪当前正在克隆但尚未完成的所有对象IdentityHashMap
中查找该对象,查看您是否已经克隆了该位。如果有,请返回在IdentityHashMap
中找到的副本null
,然后继续IdentityHashSet
中删除null
引用。您可以同时浏览源和目标的图形。每当您在源图形中找到对象时,请在IdentityHashMap
中查找它,并找出它应该映射到的对象。如果它存在于IdentityHashMap
中,并且当前在目标图中为null
,则可以将目标引用设置为在IdentityHashMap
中找到的克隆标识
版本的要点是,如果图形中的两个对象与.equals()
确定的对象相同,但由=
确定的实例不同,则哈希集
和哈希映射
将标识这两个对象,你会把不该连接的东西连接在一起。标识
版本仅当两个实例相同时才会将其视为相同,即,由==
确定的相同实例
如果你想做所有这些,但不必自己实现,你可以看看。好吧,使用任何参数化构造函数都可以做到这一点,所以我认为重新设计是合适的。如果你最终不想自己实现,有一些库会处理这一点,例如,Dozer:您必须将集合传递到深度递归克隆方法中。此外,使用HashSet也不安全,因为您可能有等于()但不相同的对象。@jtahlborn是对的。抢手货您需要一个
标识hashset
。我将相应地修改我的答案。为什么我们需要跟踪正在被复制的对象?一旦我们遇到他们,他们的副本就可以合法引用,因此不需要HashSet。IdentityHashMap会更安全。@basilev好吧,这取决于API。如果您对所有内容都没有arg构造函数,那么您是对的,您只需要HashMap
,因为一旦您获得了一个引用,您就可以进行排序了。但是如果您的实例是使用const创建的