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