2010-11-24 21 views
5

He estado trabajando con HashMap de Java últimamente y he tropezado con un comportamiento interesante. Actualmente lo estoy usando para almacenar objetos clave/valor con múltiples campos. Para ello he anulado hashCode() y equals() de la siguiente manera:Java HashMap con hash overridden hashCode() y equals() no devuelve datos

public final class TransitionState { 

private String mStackSymbol; 
private String mTransitionSymbol; 
private int mState; 

private static final int HASH_SEED = 7;  //Should be prime 
private static final int HASH_OFFSET = 31; 

//Constructor and getter methods here 

public boolean equals(TransitionState other) { 

    //Check that we aren't comparing against ourself 
    if (this == other) { 
     return true; 
    } 

    //Check that we are not comparing against null 
    if (other == null) { 
     return false; 
    } 

    //Check fields match 
    if ((mState == other.getState()) && 
     (mTransitionSymbol.equals(other.getTransitionSymbol())) && 
     (mStackSymbol.equals(other.getStackSymbol()))) { 

     return true; 

    } else { 
     return false; 

    } 
} 

public int hashCode() { 
    int intHash = HASH_SEED; 

    //Sum hash codes for individual fields for final hash code 
    intHash = (intHash * HASH_OFFSET) + mState; 
    intHash = (intHash * HASH_OFFSET) + (mTransitionSymbol == null ? 0 : mTransitionSymbol.hashCode()); 
    intHash = (intHash * HASH_OFFSET) + (mStackSymbol == null ? 0 : mStackSymbol.hashCode()); 

    return intHash; 
} 
} 

Ahora, yo soy capaz de poner los artículos en el mapa sin problema. Recuperarlos, sin embargo, es otra historia. Cada vez que intento get() desde HashMap, se devuelve NULL. Escribí un código de prueba para iterar sobre el Mapa e imprimir valores, que es donde las cosas se vuelven confusas, ya que el hashCode() de mis objetos clave coincide con el que tengo en mi mapa y la igualdad con un valor conocido devuelve verdadero. Salida de ejemplo de la siguiente manera (véase el cuarto paso de la parte inferior de la tabla):

Transition Table: 
State Symbol Stack Move 
-------------------------- 
1, a, b, (1, pop) with key hashcode 212603 and value hashcode 117943 
0, b, a, (0, pop) with key hashcode 211672 and value hashcode 117912 
1, b, z, (1, push) with key hashcode 212658 and value hashcode 3459456 
0, a, b, (0, pop) with key hashcode 211642 and value hashcode 117912 
1, a, z, (0, push) with key hashcode 212627 and value hashcode 3459425 
0, a, a, (0, push) with key hashcode 211641 and value hashcode 3459425 
0, a, z, (0, push) with key hashcode 211666 and value hashcode 3459425 
0, b, z, (1, push) with key hashcode 211697 and value hashcode 3459456 
1, b, a, (1, pop) with key hashcode 212633 and value hashcode 117943 
1, b, b, (1, push) with key hashcode 212634 and value hashcode 3459456 

ababba 

Transition from (0, a, z) with hashcode 211666 
transition.equals(new TransitionState(0, "a", "z")) = true 
HashMap containsKey() = false 
Transition not found 
false 

Como se puede ver, los hashcodes los partidos clave con una entrada en el mapa, pero estoy siendo dicho que es que no existe. Intenté depurar en el método containsKey() de HashMap, que hace un get() que se marca para NULL. Entrar en el get() muestra que el ciclo solo se está ejecutando una vez antes de devolver NULL.

Entonces, ¿es este un problema de HashMap (probablemente no) o (más probable) qué podría estar haciendo mal? Gracias de antemano por su ayuda.

Respuesta

19

No ha anulado equals adecuadamente ... necesita

public boolean equals(Object other) 

Actualmente estás sobrecarga ella.

Básicamente, la verdadera anulación todavía podría hacer el mismo trabajo que el existente, pero con una prueba primero:

if (!(other instanceof TransitionState)) 
{ 
    return false; 
} 
TransitionState otherState = (TransitionState) other; 
// Now do the rest of the comparison 

Tenga en cuenta que no es necesario el cheque por nula en este caso, como se fallaría la prueba instanceof.

En estos días de Java le permite añadir una anotación para indicar al compilador que realmente está tratando de reemplazar un método padres:

@Overrides 
public boolean equals(Object other) 

Ahora el compilador le dirá si comete un error tipográfico en el nombre o obtener la firma incorrecta.

+2

la razón perfecta por la cual '@ Overrides' siempre debe usarse. –

+0

@matt b: estaba a punto de agregar que :) –

+1

Dios mío, tienes razón. Estaba tan concentrado en la implementación que lo más simple me atrapó. Trabajado como un encanto. Gracias por su rápida respuesta. – phobos51594

2

Jon es correcto. Además se debe hacer lo siguiente:

Haga sus variables de instancia definitiva:

private final String mStackSymbol; 
private final String mTransitionSymbol; 
private final int mState; 

Si no puede hacerlo, entonces asegúrese de que no va a cambiar los valores de estas variables después de poner los elementos en el mapa. Si el estado cambia después de ponerlos en el mapa, no podrá volver a sacarlos (al menos no por sus valores originales).

+0

Estaba pensando en eso, pero opté por diseñar toda la clase de modo que no se pueda modificar (sin setters, todos los miembros son privados, etc.). Sin embargo, sospecho que esto haría que el diseño de la clase sea más evidente para alguien más adelante. – phobos51594

Cuestiones relacionadas