2011-01-06 13 views
21

Tengo un HashMap:Java HashMap.containsKey() no llamar iguales()

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

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

El código para 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; 
    } 

En mi IDE puse puntos de interrupción en equals() pero nunca se ejecuta. ¿Por qué?

Respuesta

38

Intenta poner un punto de interrupción en hashCode().

Si hashCode() de dos objetos en un mapa devuelve el mismo número, se invocará igual para determinar si son realmente iguales.

+14

Más específicamente, si implementa el método 'igual', debe implementar' hashcode' uno ... como está escrito en el manual :) –

+7

Encontré una manera fácil de usar el asistente en Eclipse, simplemente ejecutando Source | Genera hashCode() y es igual a(). –

3

JVM comprueba el cubo hashcode del código hash de ese objeto, si hay más objetos con el mismo código hash, solo entonces, se ejecutará el método equals(). Y, el desarrollador debe seguir el contrato correcto entre los métodos hashCode() y equals().

5

Solo si se utilizan 2 hashCodes iguales, se llamará a equals() durante las teclas de bucle.

3

Sólo si 2 hashcodes igual, igual será llamado() durante teclas de bucle.

esta es la respuesta correcta ... o casi. Precisamente, si dos códigos hash colisionan (siendo el mismo asegura que están obligados a colisionar bajo la correcta impl de hashmap), solo se realiza la comprobación de igualdad.

1

Por cierto, su método igual es probablemente incorrecto. En caso de que se invalide el caso LotWaferBean, su método equals aceptará la instancia de la subclase, pero ¿su subclase también lo hará?

Es mejor debería decir:

@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

Como se señaló Abimaran Kugathasan, la implementación HashMap utiliza hash cubos para buscar de manera eficiente las llaves, y sólo utiliza equals() para comparar las claves en el juego de hash-cubo contra la clave dada. Vale la pena señalar que las claves se asignan a los hash-buckets cuando se agregan a un HashMap. Si altera las claves en un HashMap después de agregarlas, de una manera que cambiaría su código hash, entonces no estarán en el hash-bucket correcto; e intentando usar una clave coincidente para acceder al mapa, encontrará el hash-bucket adecuado, pero no contendrá la clave alterada.

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 

Esto puede dar lugar a un comportamiento bastante extraño. Puede establecer un punto de interrupción justo antes de la clave de confirmación de mapeo (clave), y ver que el valor de la tecla coincida con una clave en el mapa, y sin embargo no se invocarán los equivalentes() de la clave y containsKey() devolverá falso.

Como se indica aquí https://stackoverflow.com/a/21601013, en realidad hay una advertencia de JavaDoc para Map con respecto al uso de tipos mutables para las claves. Los tipos de mapas sin hash no tendrán este problema en particular, pero podrían tener otros problemas cuando las claves se modifiquen en el lugar.