Question

I have a 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]);
}

The code for 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 my IDE I put breakpoints in equals() but it is never executed. Why?

Was it helpful?

Solution

Try putting a breakpoint in hashCode().

If the hashCode() of two objects in a map return the same number, then equals will be called to determine if they're really equal.

OTHER TIPS

Only if 2 hashCodes equal, equals() will be called during loop keys.

JVM checks the hashcode bucket of that object's hashcode, if there are more objects with the same hashcode, then only, the equals() method will be executed. And, the developer should follow correct contract between the hashCode() and equals() methods.

Only if 2 hashCodes equal, equals() will be called during loop keys.

this is the correct answer... or almost. Precisely, if 2 hash codes collide (being the same ensures they are bound to collide under proper hashmap impl), only then equality check is performed.

BTW, your equal method is most likely incorrect. In case LotWaferBean is overridden, your equals method will accept the subclass instance, but will your subclass also do?

It better should read:

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

As Abimaran Kugathasan noted, the HashMap implementation uses hash-buckets to efficiently look up keys, and only uses equals() to compare the keys in the matching hash-bucket against the given key. It's worth noting that keys are assigned to hash-buckets when they are added to a HashMap. If you alter keys in a HashMap after adding them, in a way that would change their hash code, then they won't be in the proper hash-bucket; and trying to use a matching key to access the map will find the proper hash-bucket, but it won't contain the altered key.

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

This can result in some pretty odd-looking behavior. You can set a breakpoint just before theMap.containsKey(theKey), and see that the value of theKey matches a key in theMap, and yet the key's equals() won't be called, and containsKey() will return false.

As noted here https://stackoverflow.com/a/21601013 , there's actually a warning the JavaDoc for Map regarding the use of mutable types for keys. Non-hash Map types won't have this particular problem, but could have other problems when keys are altered in-place.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top