web-dev-qa-db-fra.com

Java HashMap.containsKey () n'appelle pas equals ()

J'ai un hashmap:

Map<LotWaferBean, File> hm = new HashMap<LotWaferBean, File>();

LotWaferBean lw = new LotWaferBean();
... //populate lw
if (!hm.containsKey((LotWaferBean) lw)) {
  hm.put(lw, triggerFiles[l]);
}

Le code pour LotWaferBean:

@Override
public boolean equals(Object o) {
        if (!(o instanceof LotWaferBean)) {
              return false;
        }
        if (((LotWaferBean) o).getLotId().equals(lotId)
                    && ((LotWaferBean) o).getWaferNo() == waferNo) {
              return true;
        }
        return false;
  }

Dans mon IDE je mets des points d'arrêt dans equals() mais celui-ci n'est jamais exécuté. Pourquoi? 

24
Will Sumekar

Essayez de mettre un point d'arrêt dans hashCode ().

Si le hashCode () de deux objets d'une carte renvoie le même nombre, égalité sera appelée pour déterminer s'ils sont vraiment égaux.

41
LazyCubicleMonkey

Seulement si 2 hashCodes sont égaux, equals() sera appelé pendant les clés de boucle. 

5

La machine virtuelle Java vérifie le compartiment hashcode du hashcode de cet objet. S'il y a plus d'objets avec le même hashcode, alors seulement, la méthode equals () sera exécutée. De plus, le développeur doit suivre le contrat correct entre les méthodes hashCode () et equals ().

4

Seulement si 2 hashCodes sont égaux, equals () sera appelé pendant les clés de boucle.

c'est la bonne réponse ... ou presque. Précisément, si 2 codes de hachage entrent en collision (étant identiques, ils s'assemblent sous une implication hashmap appropriée), seul le contrôle d'égalité est effectué.

3
bestsss

BTW, votre méthode égale est probablement incorrecte. Si LotWaferBean est remplacé, votre méthode equals acceptera l'instance de sous-classe, mais votre sous-classe le fera-t-elle aussi?

Il vaut mieux lire:

@Override
public boolean equals(Object o) {
    if (o == null || o.getClass() != getClass()) { // << this is important
        return false;
    }

    final LotWaferBean other = (LotWaferBean)o;
    return other.getLotId().equals(lotId)
                && other.getWaferNo() == waferNo);
}
1
Mot

Comme Abimaran Kugathasan l'a noté, l'implémentation de HashMap utilise des compartiments de hachage pour rechercher efficacement des clés et utilise uniquement equals () pour comparer les clés du hachage correspondant à la clé donnée. Il est à noter que les clés sont attribuées à des paniers de hachage lorsqu'elles sont ajoutées à un HashMap . Si vous modifiez les clés dans une carte de hachage après les avoir ajoutées, de manière à modifier leur code de hachage, elles ne figureront pas dans le compartiment de hachage approprié; et si vous essayez d'utiliser une clé correspondante pour accéder à la carte, vous obtiendrez le hachage correct, mais il ne contiendra pas la clé modifiée.

class aMutableType {
   private int value;
   public aMutableType(int originalValue) {
     this.value = originalValue;
   }
   public int getValue() {
     return this.value;
   }
   public void setValue(int newValue) {
     this.value = newValue;
   }
   @Override
   public boolean equals(Object o) {
       // ... all the normal tests ...
       return this.value == ((aMutableType) o).value;
   }
   @Override
   public int hashCode() {
       return Integer.hashCode(this.value);
   }
}
...
Map<aMutableType, Integer> aMap = new HashMap<>();
aMap.put(new aMutableType(5), 3); // puts key in bucket for hash(5)
for (aMutableType key : new HashSet<>(aMap.keySet()))
    key.setValue(key.getValue()+1);  // key 5 => 6
if (aMap.containsKey(new aMutableType(6))
    doSomething();  // won't get here, even though
                    // there's a key == 6 in the Map,
                    // because that key is in the hash-bucket for 5

Cela peut entraîner un comportement assez étrange. Vous pouvez définir un point d'arrêt juste avant theMap.containsKey (theKey) et voir que la valeur de laKey correspond à une clé dans theMap. Pourtant, la clé equals () ne sera pas appelée, et containsKey () retournera false.

Comme indiqué ici https://stackoverflow.com/a/21601013 , il existe en fait un avertissement à la JavaDoc de Map concernant l'utilisation de types mutables pour les clés. Les types de cartes sans hachage n'auront pas ce problème particulier, mais pourraient avoir d'autres problèmes lorsque les clés sont modifiées sur place.

0
Karl Zimmerman