我正在爲每個實體編寫自定義克隆方法。對於深層複製,有一種方法可以檢測循環引用,還是必須手動找出並將克隆限制爲單向而不是雙向。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