2011-03-01 6 views
5

Je suis en train d'écrire une méthode de clone personnalisé pour chaque entité. pour une copie en profondeur, il existe un moyen de détecter les références circulaires ou dois-je le déterminer manuellement et limiter le clonage à unidirectionnel plutôt que bidirectionnel.Java détecte les références circulaires pendant le clonage sur mesure

Par exemple, nous utilisons un hiberner donc un objet utilisateur dispose d'une référence à l'adresse et l'adresse a une référence à l'utilisateur. Essayer de voir si faire une copie complète d'adresse ainsi que l'utilisateur est possible sans se heurter à des problèmes de référence circulaire

+0

pour gérer les références circulaires une IdentityMap peut être utilisé. Cela permet de garder une trace de chaque objet qu'il trouve et quand vous sérialisez ou copiez des données, vous pouvez l'utiliser pour vous assurer de gérer correctement les objets répétés. par exemple. vous pourriez avoir le même objet plusieurs fois dans une structure et vous ne voulez pas les transformer en objets différents. –

Répondre

7

Pour mettre en œuvre, vous avez besoin d'une carte de références à des objets déjà cloné. Nous avons mis quelque chose de profond clone comme ceci:

Dans notre classe de base de l'entité:

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. 
    ... 
} 
+1

HashMap est une idée terrible à utiliser dans un tel cas (les deux peuvent être bidon et c'est lent). IdentityHashMap est beaucoup mieux. – bestsss

+0

+1 Super commentaire. Mise à jour de ma réponse et correction de mon propre code :) Mais je dois admettre que HashMap n'est pas lent. À mon humble avis, c'est incroyable. – Daniel

+0

La différence de lenteur entre HashMap et IdentityHashMap dépend des implémentations .hashCode et .equals des clés. Si elles sont mauvaises, HashMap devient lent (et dans cet algorithme, vous pouvez obtenir des résultats erronés, comme fusionner différents objets parce qu'ils sont égaux). –

1

@ Daniel: Merci pour cette excellente réponse!

Je viens d'utiliser votre code et modifié un peu basé sur mes besoins ce qui le rend plus facile à utiliser avec des sous-classes. Peut-être que quelqu'un d'autre est intéressé par cela aussi bien alors voici mon code pour la classe de base:

/** 
* 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; 

Que mettre dans les sous-classes:

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

Il est pas parfait pour l'instant, mais il obtient le travail bien pour l'instant :)

Questions connexes