2011-01-06 99 views
21

我有一个HashMap:的Java HashMap.containsKey()不调用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]); 
} 

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

在我的IDE我把断点equals()但永远不会执行。为什么?

回答

38

尝试在hashCode()中放置断点。

如果map中两个对象的hashCode()返回相同的数字,那么会调用equals来确定它们是否真的相等。

+14

更具体地说,如果你实现`equals`方法,你应该实现`hashcode`一个......就像手册中所写的那样:) – 2011-01-06 04:38:21

3

JVM检查该对象哈希码的哈希码桶,如果有更多的对象具有相同的哈希码,那么只会执行equals()方法。而且,开发者应该遵循hashCode()和equals()方法之间的正确契约。

5

仅当2个hashCode相等时,equals()将在循环密钥期间被调用。

3

仅当2个hashCodes相等时,equals()将在循环密钥期间调用。

这是正确的答案......或差不多。确切地说,如果2个散列码发生冲突(同样可以确保它们在适当的散列表impl下发生碰撞),那么只有进行相等性检查。

1

顺便说一句,你的平等方法很可能是不正确的。如果LotWaferBean被覆盖,你的equals方法将接受子类实例,但是你的子类也可以吗?

它更好地应阅读:

@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

由于Abimaran Kugathasan指出,HashMap的实现使用哈希桶高效地查找键,并且只使用equals()方法来比较匹配的哈希桶的钥匙反对给定的关键。值得注意的是,当密钥被添加到HashMap时,密钥被分配给散列桶。如果您在添加哈希映射后更改哈希映射中的键值,将会改变其哈希码,那么它们将不在正确的哈希桶中;并尝试使用匹配键访问映射将找到正确的哈希桶,但它不会包含已更改的键。

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 

这可能会导致一些非常奇怪的行为。您可以在Map.containsKey(theKey)之前设置一个断点,并查看该Key的值是否与该映射中的键匹配,但该键的equals()将不会被调用,并且containsKey()将返回false。

如这里所指出的https://stackoverflow.com/a/21601013,实际上JavaDoc有关使用可变类型键的Map的警告。非哈希映射类型不会有这个特殊问题,但当键就地改变时可能会有其他问题。