2010-07-27 6 views
1
assertEquals(def.getMengs(), exp.getMengs()); 

échoue, les rapports: prévu: java.util.HashSet < [... donc mourir geht Legende ... ... la légende ...]> mais était: java.util.HashSet < [ ... donc la légende l'a ...]>Pourquoi ma méthode equals ne fonctionne pas?

En effet, à travers le débogueur, je vois que les deux ensembles ne contiennent qu'une signification avec objId = 1 pour les deux. Je m'attendais le code suivant dans la classe Signification (@Entity) pour garantir que ce qui précède fonctionne.

@Override 
public boolean equals(Object object) { 
    if (!(object instanceof Meaning)) { 
     return false; 
    } 
    Meaning other = (Meaning) object; 
    if (other != null && objId == other.objId) return true; 
    return false; 
} 

@Override 
public int hashCode() { 
    int hash = 7; 
    hash = 67 * hash + this.objId; 
    return hash; 
} 

En effet, ce test passe:

db.insert(admin); 
    final Meaning meng = new Meaning(admin, new Expression("essen")); 
    meng.setObjId(11); 
    final Meaning meng1 = new Meaning(admin, new Expression("mangiare")); 
    meng1.setObjId(11); 
    assertEquals(meng,meng1); 

Alors, quel pourrait être mon problème? Thery sont tous deux des HashSets, ils sont tous deux de la même taille, et les objets qu'ils contiennent sont égaux. En effet

  assertEquals(def.getMengs().iterator().next(), exp.getMengs().iterator().next()); 

avant qu'il ne passe. Toutefois, ce ne sera pas (mais je ne sais pas pourquoi):

assertTrue(def.getMengs().containsAll(exp.getMengs())); 

Ainsi, il est le problème.

est ici le code de test:

try{ 
      db.insertWords(toEnumMap(mengs[i],admin)); 
     }catch(Exception e){ 
      fail(e.getMessage()); 
     } 
     final Expression exp = db.get(Expression.class, mengs[i][0]); 
     testGender(exp, mengs[i][2]); 

     final Expression def = db.get(Expression.class, mengs[i][1]); 
     assertNotNull(def); 

     assertEquals(def.getMengs().iterator().next(), exp.getMengs().iterator().next()); 
     assertEquals(exp.getMengs().size(), def.getMengs().size()); 
     assertTrue(def.getMengs().containsAll(def.getMengs())); 
     assertTrue(def.getMengs().containsAll(exp.getMengs())); 
     assertEquals(def.getMengs(), exp.getMengs()); 

db.get enveloppe juste em.find. InsertWords devrait persister def et exp.

public void insertWords(EnumMap<Input, MemoEntity> input) throws MultipleMengsException { 
    insert(input.get(Input.expression)); //INSERT OR IGNORE 
    final boolean isNewDef = insert(input.get(Input.definition)); 

    final Expression def = get(Expression.class, input.get(Input.definition).getId()); 
    final Expression exp = get(Expression.class, input.get(Input.expression).getId()); 
    final MUser usr = get(MUser.class, input.get(Input.user).getId()); 

    final Set<Meaning> mengs = getMengs(usr,def,isNewDef); 
    if (mengs == null) {//is new to the user 
     final Meaning meng = new Meaning(usr, exp, def); 
     insert(meng); 

    } else { //old meaning 
     if (mengs.size() > 1) throw new MultipleMengsException(mengs); 
     else{ 
      final Meaning meng = mengs.iterator().next(); 
      meng.addExp(exp); 
      meng.setLastPublishedDate(null); //reschedule 
     } 
    } 
    Logger.getAnonymousLogger().log(Level.INFO, "inserted pair <{0},{1}>", new String[]{exp.getExpression(), def.getExpression()}); 
} 

public boolean insert(final MemoEntity entity) { 
     if (em.find(entity.getClass(), entity.getId()) == null) { 
      et.begin(); 
      em.persist(entity); 
      et.commit(); 
      return true; 
     } 
     return false; 
    } 

public <MemoEntity> MemoEntity get(final Class<MemoEntity> entityClass, final Object primaryKey) { 
     return em.find(entityClass, primaryKey); 
    } 
+0

'int hash = 7; hash = 67 * hash' ??? Pourquoi pas 'int hash = 469 + obj.Id'? Ou juste 'return obj.Id'? Ajouter une constante au hashcode est inutile, – NullUserException

+1

@NullUserException: 'return 4;' est la bonne variante! – Roman

Répondre

0

Vous utilisez des entités mise en veille prolongée, de sorte que vous ne devriez jamais se référer à des variables membres directement, car ils peuvent être chargés à partir paresseusement la base de données. Par conséquent, votre méthode equals/hashCode doit utiliser getObjId() au lieu de objId.

également, comme mentionné dans un autre article, si votre objId est un type d'objet (Integer, Long), vous ne devriez pas utiliser "==".

-1

Le problème est que containsAll compare les références aux objets (en utilisant ==) au lieu d'appeler .equals. HashSet.equals utilise containsAll en interne selon la documentation.

Une solution possible est de dériver votre propre classe à partir de HashSet et de remplacer la méthode containsAll.

+3

-1 '.containsAll()' utilise '.equals()' pour les comparaisons – NullUserException

+0

@NullUserException, alors où est le problème? Vous avez dit que la constante n'est pas nécessaire, mais ce n'est pas le problème. – simpatico

+0

@simpatico est votre travail '.equals()'? – NullUserException

0

Si vous hachez par objId, il ne doit pas être modifiable (comme le suggère la méthode setObjId). En particulier, si vous insistez pour avoir cette méthode, vous ne devriez pas l'appeler après avoir placé un objet dans un hashset.

+0

hmm..J'ai ajouté la méthode setObjId juste pour tester, maintenant après que j'ai eu le problème. Je suis d'accord que ça ne devrait pas être mutable. Cependant, vous ne répondez pas du tout à la question, n'est-ce pas? – simpatico

+1

* Si * vous changez objId après l'avoir placé dans un HashSet (que je ne sais pas si vous ne le faites pas parce que vous n'incluez pas tout le code), * alors * c'est le problème (donc je * réponds à la question , conditionnellement :)). –

+0

Je ne change jamais l'objId, sauf dans ce code de test (qui ne fait pas partie du problème). – simpatico

1

Si

assertTrue(def.getMengs().containsAll(exp.getMengs())); 

passe, l'explication la plus probable est que def.getMengs() contient tous les éléments de exp.getMengs(), plus un autre pas dans celui-ci.

inverser le sens, à savoir

assertTrue(exp.getMengs().containsAll(def.getMengs())); 

ou simplement

assertEqual(exp.getMengs().size(), def.getMengs().size()); 

Modifier

Je vois que je l'ai mal lu votre question. Cependant, cela clarifie la situation. La méthode égale vérifie trois choses. 1) Les deux sont de type Set. 2) Même taille et 3) Que "a" contient tous les éléments de "b".

Vous semblez échouer le dernier. En effet, puisque faire containsAll sur un HashSet avec lui-même échoue doit être la méthode equals sur Signification. La lecture du code (Java 6) pour les méthodes containsAll et contains sur les ensembles montre clairement que la méthode hashCode n'est pas utilisée à cette fin.

+0

Non, ce n'est pas le cas. Le débogueur a montré qu'ils ont la même taille, et en effet la taille affirme passer. – simpatico

+0

Fait intéressant, cela échoue aussi: assertTrue (def.getMengs(). ContainsAll (def.getMengs())); – simpatico

3

HashCode et égal pour les entités sont mieux écrits sans utiliser l'ID de substitution pour la comparaison, mais implémenter plutôt la comparaison en utilisant les attributs métier de l'objet ou seulement les clés naturelles. Voir ce SO question pour plus de détails.

EDIT: En ce qui concerne pourquoi cela ne fonctionne pas, je suppose que vous modifiez l'objet après l'avoir ajouté au HashSet, en particulier en changeant l'ID. Cela provoquera l'échec de la méthode contains, car elle utilise le hashCode pour localiser l'objet en tant que clé dans la table de hachage, et si l'ID a changé, il cherchera au mauvais endroit dans le hashMap sous-jacent.

+0

vous ne répondez pas à la question. – simpatico

+0

@simpatico - J'y allais juste pour vérifier les sources du JDK. S'il vous plaît voir mon edit. – mdma

+0

Je ne vois pas comment changer l'objet (autre que le hashcode ou objId) cause le problème. Je posterai le code de test. – simpatico

1

Votre objId est-il un nombre entier ou long? Ensuite, votre problème est probablement lié à autoboxing dans la méthode equals:

objId == other.objId 

Cela fonctionne pour les petites constantes comme dans votre premier test puisque ceux-ci sont mises en cache. En général, vous devez utiliser la méthode égale dans ce cas. Cette ligne dans la méthode equals pourrait être mieux écrit:

return objId == null ? this == other : objId.equals(other.objId); 
Questions connexes