Java HashMap работает, но содержит ключ - PullRequest
14 голосов
/ 09 июля 2009

Я пытаюсь найти ключ в HashMap. Я могу напечатать выбранный ключ, используя 'get', но когда я использую 'содержащий ключ' в операторе 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;
    }

}

Ответы [ 9 ]

23 голосов
/ 09 июля 2009

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

5 голосов
/ 09 июля 2009

Как описано здесь, вы должны переопределить метод 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);
    }

Из равных равных

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

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

2 голосов
/ 09 июля 2009

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

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

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

Edit: Чтобы уточнить, когда я говорю, что 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, или, что еще хуже, эти методы полностью отключены), и приведет к поведению, которое вы наблюдали.

2 голосов
/ 09 июля 2009

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

2 голосов
/ 09 июля 2009

В Location class, убедитесь, что вы переопределяете hashCode и равно методов.

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

1 голос
/ 09 июля 2009

Пик в исходном коде для реализации 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 Получить: Успех содержит: правда
Ключ: HashMapTest $ KeyCheck @ 5 Получить: Успешный содержит: правда
Key: HashMapTest $ KeyCheck @ a Get: null Содержит: false
Key: HashMapTest $ KeyCheck @ 5 Get: null Содержит: false

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

1 голос
/ 09 июля 2009

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

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

1 голос
/ 09 июля 2009

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

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

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

0 голосов
/ 28 ноября 2012

я думаю, что иногда вам нужен хеш-код, а иногда нет, поэтому я думаю, что таким образом вы можете выключить проверку хэш-кода, когда хотите купить, изменив хэш-код для всех объектов, которые вы хотите установить на 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...