Java HashMap работает, но содержитKey не

Я пытаюсь найти ключ в HashMap. Я могу напечатать выбранный ключ, используя "get", но когда я использую "containsKey" в выражении if, он не найден.

Я ЗНАЮ, что ключ присутствует на Карте, но он продолжает возвращать false. Любые идеи людей?

Мой код:

public static boolean checkLowerStructuralSupport(Location location) {

    boolean hasSupport = false;

    Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);

    System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works

    if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
        hasSupport = true;
    } else {
        hasSupport = false;
    }

    return hasSupport;
}

Вот код для класса Location:

public class Location {

    protected int _x;
    protected int _y;
    protected int _z;

    public Location(int xAxis, int yAxis, int zAxis) {
        this._x = xAxis;
        this._y = yAxis;
        this._z = zAxis;
    }

    public void equals() {
        //not implemented yet
    }

    public void HashCode() {
        //not implemented yet
    }

    public String toString() {
        String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
        return locationString;
    }

    public void setX(int XAxis) {
        this._x = XAxis;
    }

    public int getX() {
        return this._x;
    }

    public void setY(int YAxis) {
        this._y = YAxis;
    }

    public int getY() {
        return this._y;
    }

    public void setZ(int ZAxis) {
        this._z = ZAxis;
    }

    public int getZ() {
        return this._z;
    }

}

Ответы

Ответ 1

Вы должны убедиться, что класс Location правильно реализовал методы hashCode() и equals(Object) (документация). То есть, если два Location объекта эффективно равны, они должны совместно использовать общий хеш-код, а метод equals должен возвращать true.

Ответ 2

Как описано здесь, вы должны переопределить метод equals (Object).

Причина, по которой get (Object) работает, заключается в том, что HashMap будет вычислять Hash для вашего класса Location и возвращает объект, на который указывает hascode.

containsKey (Object) вычисляет хэш-ключ и получает объект, на который указывает хеш. Объект из HashMap будет сравниваться с объектом, который вы ввели. Для этого используется метод equals. Когда вы не переопределяете метод equals, возвращается true, когда ссылка объекта на тот же экземпляр.

Из HashMap

/** 
 * Check for equality of non-null reference x and possibly-null y. 
 */
static boolean eq(Object x, Object y) {
    return x == y || x.equals(y);
}

Из объекта

public boolean equals(Object obj) {
    return (this == obj);
    }

Из javadoc равных

Метод equals для класса Object реализует наиболее дискриминационные возможное отношение эквивалентности на объекты; то есть для любого непустого опорные значения x и y, этот метод возвращает true тогда и только тогда, когда x и y обратитесь к одному и тому же объекту (x == y имеет значение true).

Обратите внимание, что обычно необходимо переопределять метод hashCode всякий раз этот метод переопределяется, чтобы поддерживать общий hashCode, в котором говорится, что равные объекты должны иметь равный хэш коды.

Ответ 3

В классе Местоположение убедитесь, что вы переопределяете методы hashCode и equals.

Если да, можете ли вы опубликовать их?

Ответ 4

containsKey использует этот метод, чтобы сравнить параметр с записями в наборе ключей. Поэтому класс Location должен иметь метод equals, который хорош. Метод по умолчанию равен методу в java.lang.Object возвращает true только тогда, когда оба объекта являются одним и тем же объектом. В этом случае у вас, вероятно, есть два разных экземпляра, которые нужно сравнить, и им нужен собственный метод equals.

Ответ 5

Единственное, что я могу придумать, это приведет к тому, что состояние supportingLocation каким-то образом мутируется между вызовом get(...) и containsKey(...).

Предполагая, что фрагмент кода, который вы опубликовали, является точным кодом, вызывающим проблемы, единственное место, которое может возникнуть, - это если один из Location#getZ(...), Location#hashCode() или Location#equals(Object) изменяет состояние местоположения (или конструктор местоположения, или один из этих методов запускает поток, который случайным образом изменяет состояние экземпляра Location, но я думаю, что мы можем это исключить).

Не могли бы вы проверить, что ни один из вышеперечисленных методов не изменяет состояние экземпляра supportingLocation? Хотя я не знаком с самим классом Location, я бы рискнул предположить, что такой класс идеально идеален.

Изменить: Чтобы пояснить, когда я говорю, что Location#getZ() и т.д. Не изменяют местоположение, я имею в виду:

Location x = new Location(1,2,3);
Location y = new Location(1,2,3);

boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();

В конце концов, eq1 должен быть равен eq1, а hash1 должен быть равен hash2. Если это не так, getZ() мутирует состояние x (или равно или hashCode, или, что еще хуже, эти методы полностью отключены) и приведет к наблюдаемому поведению.

Ответ 6

Оба get() и containsKey() используют метод Location class hashCode(). Метод equals() не вызывается, если не существует хеш-столкновение. (таким образом, HashMap get() не будет использовать equals() в каждой ситуации.)

Для вашего класса Location, случайно ли вы выполнили свою собственную версию hashCode()? Метод hashCode() должен быть тщательно реализован. Джошуа Блох описал все подробности в книге "Эффективная Java", часть из которых находится в сети... Я пойду, найду ссылку на эти главы примеров: Эффективная Java Примеры разделов. Вам нужна глава 3.

Как я спросил в комментарии к вопросу, откуда взялась ваша переменная _levels? Я не вижу, чтобы это декларировалось внутри этого метода и вашего именования (префикс подчеркивания, вы импортируете это соглашение с другого языка?) Предполагает, что он "живет" вне этого метода. Возможно, другой код меняет его во время выполнения? Пожалуйста, сообщите нам, когда вы его решите; ожидание убивает меня.

Ответ 7

Чтобы избежать проблем, методы equals() и hashCode() должны быть согласованными и соответствовать требованиям (как указано в других разделах).

Кроме того, hashCode() должен не полагаться на изменяемые члены, иначе ваш расчетный хэш-код может измениться, и это влияет на внутреннюю работу HashMap. Это проявится в невозможности извлечь материал из коллекций Hash*.

Ответ 8

Возьмите пик в исходном коде для реализации HashMap. И get, и containsKey используют методы hasCode() и equals() вашего ключевого объекта.

Единственное реальное различие, и, как было указано, это тривиальная проверка нуля, находится в сравнении:

получим:

((k = e.key) == key || key.equals(k))

ContainsKey:

((k = e.key) == key || (key != null && key.equals(k)))

где e имеет тип Entry для HashMap.

Итак, если у вас нет сильных реализаций hashCode() и/или equals(), у вас возникнет проблема. Кроме того, если ваши ключи были мутированы (я вижу, что вы не объявили окончательные поля класса), у вас может возникнуть проблема.

Возьмем следующий пример:

public class HashMapTest {
    static class KeyCheck {
        int value;
        public KeyCheck(int value) { this.value = value; }
        public void setValue(int value) { this.value = value; }
        @Override public int hashCode() { return value; }
        @Override public boolean equals(Object o) {
            return ((KeyCheck)o).value == this.value;
        }
    }

    public static void main(String args[]) {
        HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
        KeyCheck k1 = new KeyCheck(5);
        KeyCheck k2 = new KeyCheck(5);

        map.put(k1, "Success");

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));

        k1.setValue(10);

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));
    }
}

Это напечатает:

Ключ: HashMapTest $KeyCheck @5 Получить: Успех Содержит: true
Ключ: HashMapTest $KeyCheck @5 Получить: Успех Содержит: true
Ключ: HashMapTest $KeyCheck @a Get: null Содержит: false
Ключ: HashMapTest $KeyCheck @5 Get: null Содержит: false

Как вы можете видеть, в этом случае изменчивость вызвала изменение hashCode(), что разрушило все.

Ответ 9

Я думаю, что когда-нибудь вам понадобится хеш-код, а иногда и не так, я думаю, что вы можете включить проверку хеш-кода, когда хотите купить изменение хеш-кода для всех объектов, которые вы хотите 0

public class sample(){
    @JsonIgnore
    private int hashCode = super.hashCode();

    public void setHashCode(int hashCode){
        this.hashCode = hashCode;
    }    

    @Override
    public int hashCode(){
        return this.hashCode;
    }    

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ReflectObject other = (ReflectObject) obj;
        if (this.hashCode != other.hashCode) {
            return false;
        }
        return true;
    }
}