我正在为每个实体编写自定义克隆方法。对于深层复制,有一种方法可以检测循环引用,还是必须手动找出并将克隆限制为单向而不是双向。Java在自定义克隆期间检测循环引用
例如,我们使用hibernate,因此User对象具有对Address的引用,Address对User有引用。尝试查看是否可以进行地址和用户的深层副本而不会遇到循环参考问题
我正在为每个实体编写自定义克隆方法。对于深层复制,有一种方法可以检测循环引用,还是必须手动找出并将克隆限制为单向而不是双向。Java在自定义克隆期间检测循环引用
例如,我们使用hibernate,因此User对象具有对Address的引用,Address对User有引用。尝试查看是否可以进行地址和用户的深层副本而不会遇到循环参考问题
要实现此目的,您需要一个对已克隆对象的引用的Map。我们实现深克隆是这样的:
在我们的实体基础类:
public void deepClone() {
Map<EntityBase,EntityBase> alreadyCloned =
new IdentityHashMap<EntityBase,EntityBase>();
return deepClone(this,alreadyCloned);
}
private static EntityBase deepClone(EntityBase entity,
Map<EntityBase,EntityBase> alreadyCloned) {
EntityBase clone = alreadyCloned.get(entity);
if(clone != null) {
return alreadyClonedEntity;
}
clone = newInstance(entity.getClass);
alreadyCloned.put(this,clone);
// fill clone's attributes from original entity. Call
// deepClone(entity,alreadyCloned)
// recursively for each entity valued object.
...
}
在这种情况下使用HashMap是一个可怕的想法(两者都可能是假的,而且速度很慢)。 IdentityHashMap好得多。 – bestsss 2011-03-01 18:38:34
+1很棒的评论。更新了我的答案并修复了我自己的代码:)但是我不得不承认HashMap并不慢。恕我直言,这是令人难以置信的快速。 – Daniel 2011-03-01 19:04:46
HashMap和IdentityHashMap之间的慢度差异取决于密钥的.hashCode和.equals实现。如果这些不好,HashMap变慢(在这个算法中,你可能会得到错误的结果,比如合并不同的对象,因为它们是相等的)。 – 2011-03-01 19:57:28
@Daniel:非常感谢你为这个伟大的答案!
我刚刚使用你的代码,并根据我的需要对其进行了一些修改,这使得它更容易与子类一起使用。也许别人有兴趣。也因此,这里是我的基础类代码:
/**
* Perform a deep clone.
*
* @return Deep Clone.
* @throws CloneNotSupportedException
*/
@SuppressWarnings("unchecked")
public VersionedEntityImpl deepClone() throws CloneNotSupportedException {
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned = new IdentityHashMap<VersionedEntityImpl, VersionedEntityImpl>();
return deepClone(this, alreadyCloned);
}
/**
* Perform a deep clone.
*
* @param entity
* @param alreadyCloned
* @return Deep Clone.
* @throws CloneNotSupportedException
*/
@SuppressWarnings("unchecked")
protected VersionedEntityImpl deepClone(VersionedEntityImpl entity,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException {
if (entity != null) {
VersionedEntityImpl clone = alreadyCloned.get(entity);
if (clone != null) {
return clone;
}
clone = entity.clone();
alreadyCloned.put(entity, clone);
return entity.deepCloneEntity(clone, alreadyCloned);
}
return null;
}
/**
* Method performing a deep clone of an entity (circles are eliminated automatically). Calls
* deepClone(entity,alreadyCloned) recursively for each entity valued object.
*
* @param clone
* @param alreadyCloned
* @return clone
* @throws CloneNotSupportedException
*/
protected abstract VersionedEntityImpl deepCloneEntity(VersionedEntityImpl clone,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException;
什么投入的子类:
@SuppressWarnings("unchecked")
@Override
protected VersionedEntityImpl deepCloneEntity(VersionedEntityImpl clone,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException {
// fill clone's attributes from original entity. Call
// deepClone(entity,alreadyCloned)
// recursively for each entity valued object.
if (this.associatedItems != null) {
List<SomeClass> listClone = new LinkedList<SomeClass>();
for (SomeClass someClass: this.associatedItems) {
listClone.add((SomeClass) super.deepClone(someClass, alreadyCloned));
}
((SomeOtherClass) clone).setAssociatedItems(listClone);
}
((SomeOtherClass) clone).setYetAnotherItem((YetAnotherClass) super.deepClone(this.yai, alreadyCloned));
return clone;
}
它不是完美的作为然而,但它得到现在完成的工作很好:)
要处理循环引用,可以使用IdentityMap。这将跟踪它找到的每个对象,以及当您序列化或复制数据时,您可以使用它来确保正确处理重复的对象。例如你可能在一个结构中多次拥有同一个对象,并且你不想把它们变成不同的对象。 – 2011-03-01 17:37:14