2011-01-06 8 views
21

Ich habe einen hashmap:Java HashMap.containsKey() ruft nicht equals()

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

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

Der Code für 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; 
    } 

In meiner IDE ich Stützpunkte in equals() setzen, aber es wird nie ausgeführt. Warum?

Antwort

38

Versuchen Sie einen Haltepunkt in hashCode() zu setzen.

Wenn der hashCode() von zwei Objekten in einer Map die gleiche Zahl zurückgibt, wird equals aufgerufen, um festzustellen, ob sie wirklich gleich sind.

+14

genauer gesagt, wenn Sie die 'equals' Methode implementieren, sollten Sie den' hashcode' implementieren ... wie im Handbuch geschrieben :) –

+7

Ich fand eine einfache Möglichkeit ist, den Assistenten in Eclipse zu verwenden, nur Source ausgeführt | Generiere hashCode() und equals(). –

3

JVM überprüft den Hashcode-Bucket des Hashcodes dieses Objekts. Wenn es mehr Objekte mit demselben Hashcode gibt, wird nur die equals() -Methode ausgeführt. Und der Entwickler sollte den richtigen Vertrag zwischen den Methoden hashCode() und equals() einhalten.

5

Nur wenn 2 HashCodes gleich sind, wird equals() während der Loop-Keys aufgerufen.

3

Nur wenn 2 Hashcodes gleich, gleich() wird während der Loop-Tasten aufgerufen werden.

Dies ist die richtige Antwort ... oder fast. Genauer gesagt, wenn zwei Hash-Codes kollidieren (da sie gleich sind, stellt sie sicher, dass sie unter dem richtigen Hash-Impl kollidieren), wird nur dann eine Gleichheitsprüfung durchgeführt.

1

BTW, Ihre gleiche Methode ist höchstwahrscheinlich falsch. Falls LotWaferBean außer Kraft gesetzt wird, akzeptiert Ihre equals Methode die Unterklasseninstanz, aber wird Ihre Unterklasse das auch tun?

Es sollte besser lauten:

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

Wie Abimaran Kugathasan erwähnt, ist die HashMap Implementierung verwendet Eimer Hash-effizient Schlüssel aufblicken, und nur verwendet equals() die Schlüssel in der entsprechenden Hash-Eimer vergleichen gegen den angegebenen Schlüssel. Es ist erwähnenswert, dass Schlüssel den Hash-Buckets zugewiesen werden, wenn sie zu einer HashMap hinzugefügt werden. Wenn Sie Schlüssel in einer HashMap ändern, nachdem Sie sie hinzugefügt haben, und zwar auf eine Weise, die ihren Hash-Code ändert, dann sind sie nicht im richtigen Hash-Bucket; Wenn Sie versuchen, einen übereinstimmenden Schlüssel für den Zugriff auf die Map zu verwenden, wird der richtige Hash-Bucket gefunden, der geänderte Schlüssel wird jedoch nicht enthalten sein.

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 

Dies kann zu einem ziemlich merkwürdigen Verhalten führen. Sie können einen Haltepunkt kurz vor theMap.containsKey (theKey) setzen und sehen, dass der Wert von theKey mit einem Schlüssel in der Map übereinstimmt, und dennoch wird der Wert equals() des Schlüssels nicht aufgerufen, und containsKey() gibt false zurück.

Wie hier erwähnt https://stackoverflow.com/a/21601013, gibt es tatsächlich eine Warnung der JavaDoc für Map bezüglich der Verwendung von veränderbaren Typen für Schlüssel. Nicht-Hash-Map-Typen haben dieses spezielle Problem nicht, können aber andere Probleme haben, wenn Schlüssel direkt geändert werden.

Verwandte Themen