I am writing a custom clone method for each entity. for deep copy is there a way to detect circular references or do I have to manually figure it out and restrict cloning to be unidirectional instead of bidirectional.
For example we use hibernate an hence a User object has a reference to Address and Address has a reference to User. Trying to see if doing a deep copy of Address as well as User is possible without running into circular reference issues
To implement this, you need a Map of references to already cloned objects. We implemented deep clone something like this:
In our entity base class:
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.
...
}
@Daniel: Thank you very much for this great answer!
I just used your code and modified it a little based on my needs which makes it easier to use with subclasses. Maybe someone else is interested in that as well so here is my code for the base class:
/**
* 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;
What to put into the subclasses:
@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;
}
It is not perfect as of yet but it gets the job done nicely for now :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With