Java HashMap containsKey возвращает false для существующего объекта
у меня есть HashMap для хранения объектов:
private Map<T, U> fields = Collections.synchronizedMap(new HashMap<T, U>());
но, при попытке проверить наличие ключа,containsKey
возвращает false
.equals
и hashCode
методы реализованы, но ключ не найден.
При отладке фрагмента кода:
return fields.containsKey(bean) && fields.get(bean).isChecked();
Я:
bean.hashCode() = 1979946475
fields.keySet().iterator().next().hashCode() = 1979946475
bean.equals(fields.keySet().iterator().next())= true
fields.keySet().iterator().next().equals(bean) = true
но
fields.containsKey(bean) = false
что могло вызвать такие странные behavioure?
public class Address extends DtoImpl<Long, Long> implements Serializable{
<fields>
<getters and setters>
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + StringUtils.trimToEmpty(street).hashCode();
result = prime * result + StringUtils.trimToEmpty(town).hashCode();
result = prime * result + StringUtils.trimToEmpty(code).hashCode();
result = prime * result + ((country == null) ? 0 : country.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Address other = (Address) obj;
if (!StringUtils.trimToEmpty(street).equals(StringUtils.trimToEmpty(other.getStreet())))
return false;
if (!StringUtils.trimToEmpty(town).equals(StringUtils.trimToEmpty(other.getTown())))
return false;
if (!StringUtils.trimToEmpty(code).equals(StringUtils.trimToEmpty(other.getCode())))
return false;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
return true;
}
}
4 ответов
вы не должны изменять ключ после того, как вставили его в карту.
Edit: я нашел экстракт javadoc в карта :
Примечание: необходимо проявлять большую осторожность, если изменяемые объекты используются в качестве ключей карты. Поведение карты не задается, если значение объекта изменяется таким образом, что влияет на сравнения equals, в то время как объект является ключом на карте.
пример с простым классом-оболочкой:
public static class MyWrapper {
private int i;
public MyWrapper(int i) {
this.i = i;
}
public void setI(int i) {
this.i = i;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return i == ((MyWrapper) o).i;
}
@Override
public int hashCode() {
return i;
}
}
и тест :
public static void main(String[] args) throws Exception {
Map<MyWrapper, String> map = new HashMap<MyWrapper, String>();
MyWrapper wrapper = new MyWrapper(1);
map.put(wrapper, "hello");
System.out.println(map.containsKey(wrapper));
wrapper.setI(2);
System.out.println(map.containsKey(wrapper));
}
выход :
true
false
Примечание: Если вы не переопределите hashcode (), то вы получите true только
как Арно Denoyelle указывает, изменяя ключ может иметь такой эффект. The причина это containsKey
заботится о ведре ключа в хэш-карте, в то время как итератор этого не делает. Если первый ключ на вашей карте-не обращая внимания на ведра-именно тот, который вам нужен, тогда вы можете получить поведение, которое видите. Если на карте есть только одна запись, это, конечно, гарантировано.
представьте себе простую карту с двумя ведрами:
[0: empty] [1: yourKeyValue]
в итератор выглядит так:
- повторите все элементы в ведре 0: Нет
- повторите все элементы в ведре 1: только один
yourKeyValue
на containsKey
метод, однако, выглядит так:
-
keyToFind
естьhashCode() == 0
, поэтому позвольте мне посмотреть в ведра 0 (и только есть). О, она пуста-вернись!--7-->
в самом деле, даже если ключ остается в том же ведро, ты будешь!--13-->еще есть такая проблема! Если вы посмотрите на реализацию HashMap
, вы увидите, что каждая пара ключ-значение хранится вместе с хэш-кодом ключа. Когда карта хочет проверить сохраненный ключ на входящий, она использует этот hashCode и ключа equals
:
((k = e.key) == key || (key != null && key.equals(k))))
это хорошая оптимизация, так как это означает, что ключи с разными хэш-кодами, которые сталкиваются в одном ведре, будут рассматривается как неравный очень дешево (просто int
сравнение). Но это также означает, что изменение ключа - который не изменит сохраненный e.key
поле -- разобьет карту.
отладка исходного кода java я понял, что метод containsKey проверяет две вещи на искомом ключе против каждого элемента в наборе ключей: хэш-код и равна; и он делает это в таком порядке.
Это означает, что если obj1.hashCode() != obj2.hashCode()
, он возвращает false (без оценки obj1.метод Equals(obj2). Но если ... --1-->, то возвращается obj1.equals(obj2)
вы должны быть уверены, что оба метода -может быть, вы должны переопределить их значение true для вашего определяющий критерий.
здесь SSCCE
на ваш вопрос ниже. Это работает как шарм, и это не может быть иначе, потому что ваш hashCode
и equals
методы кажутся автогенерируемые на IDE и они выглядят нормально.
Итак, ключевое слово when debugging
. Сама отладка может повредить ваши данные. Например, где-то в окне отладки вы устанавливаете выражение, которое изменяет ваш fields
объект или