2010-11-24 3 views
5

В последнее время я работаю с HashMap Java, и вы столкнулись с каким-то интересным поведением. В настоящее время я использую его для хранения объектов key/value с несколькими полями. Для этого я переопределении хэш-код() и равна() следующим образом:Java HashMap с переопределенным hashCode() и equals() возвращает данные

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

Теперь, я могу положить вещи в карте без проблем. Однако получение их - это еще одна история. Всякий раз, когда я пытаюсь получить() из HashMap, возвращается NULL. Я написал несколько тестовых кодов, чтобы перебирать их по Карте и печатать значения, в которых вещи запутываются, так как hashCode() моих ключевых объектов соответствует тому, что у меня есть на моей карте, а равенство с известным значением возвращает true. Пример вывод следующим образом (см четвертого перехода от нижней части таблицы):

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 

Как вы можете видеть, ключевое Удачное hashcodes с записью в карте, но мне говорит, что это не существует. Я попытался отлаживать метод containsKey() HashMap, который выполняет get(), который проверяется на NULL. Шаг в get() показывает, что цикл выполняется только один раз перед возвратом NULL.

Итак, это проблема HashMap (скорее всего, нет) или (скорее), что я могу делать неправильно? Спасибо заранее за вашу помощь.

ответ

19

Вы не переопределеныequals правильно ... Вам нужно

public boolean equals(Object other) 

В настоящее время вы просто перегрузки его.

В основном реальное переопределения все еще может сделать ту же работу, что и существующим, но с тестом первым:

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

Обратите внимание, что вам не нужны чеки на нуль в этом случае, как это провалило бы тест instanceof.

В эти дни Java позволяет вам добавить аннотацию, чтобы сообщить компилятору вы действительно пытаетесь переопределить родительский метод:

@Overrides 
public boolean equals(Object other) 

Теперь компилятор сообщит вам, если вы сделаете опечатку в имени или получить подпись неправильно.

+2

Идеальная причина, по которой `@ Переопределения` всегда должны использоваться. – 2010-11-24 17:48:14

+0

@matt b: Я собирался добавить это :) – 2010-11-24 17:48:48

2

Jon is correct. Кроме того, вы должны сделать следующее:

сделать переменные экземпляра окончательным:

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

Если вы не можете сделать это, то убедитесь, что вы не изменяя значения этих переменных после помещаете элементы в карту. Если состояние изменяется после того, как вы поместили их на карту, вы не сможете их снова вытащить (по крайней мере, не по их первоначальным значениям).

Смежные вопросы