I have a HashMap for storing objects:
private Map<T, U> fields = Collections.synchronizedMap(new HashMap<T, U>());
but, when trying to check existence of a key, containsKey
method returns false
.
equals
and hashCode
methods are implemented, but the key is not found.
When debugging a piece of code:
return fields.containsKey(bean) && fields.get(bean).isChecked();
I have:
bean.hashCode() = 1979946475
fields.keySet().iterator().next().hashCode() = 1979946475
bean.equals(fields.keySet().iterator().next())= true
fields.keySet().iterator().next().equals(bean) = true
but
fields.containsKey(bean) = false
What could cause such strange 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;
}
}
Debugging the java source code I realized that the method containsKey checks two things on the searched key against every element in the key set: hashCode and equals; and it does it in that order.
It means that if
obj1.hashCode() != obj2.hashCode()
, it returns false (without evaluating obj1.equals(obj2). But, ifobj1.hashCode() == obj2.hashCode()
, then it returnsobj1.equals(obj2)
You have to be sure that both methods -may be you have to override them- evaluate to true for your defined criteria.
Here is
SSCCE
for your issue bellow. It works like a charm and it couldn't be else, because yourhashCode
andequals
methods seem to be autogenerated by IDE and they look fine.So, the keyword is
when debugging
. Debug itself can harm your data. For example somewhere in debug window you set expression which changes yourfields
object orbean
object. After that your other expressions will give you unexpected result.Try to add all this checks inside your method from where you got
return
statement and print out their results.As Arnaud Denoyelle points out, modifying a key can have this effect. The reason is that
containsKey
cares about the key's bucket in the hash map, while the iterator doesn't. If the first key in your map --disregarding buckets -- just happens to be the one you want, then you can get the behavior you're seeing. If there's only one entry in the map, this is of course guaranteed.Imagine a simple, two-bucket map:
The iterator goes like this:
yourKeyValue
The
containsKey
method, however, goes like this:keyToFind
has ahashCode() == 0
, so let me look in bucket 0 (and only there). Oh, it's empty -- returnfalse.
In fact, even if the key stays in the same bucket, you'll still have this problem! If you look at the implementation of
HashMap
, you'll see that each key-value pair is stored along with the key's hash code. When the map wants to check the stored key against an incoming one, it uses both this hashCode and the key'sequals
:This is a nice optimization, since it means that keys with different hashCodes that happen to collide into the same bucket will be seen as non-equal very cheaply (just an
int
comparison). But it also means that changing the key -- which will not change the storede.key
field -- will break the map.You shall not modify the key after having inserted it in the map.
Edit : I found the extract of javadoc in Map :
Example with a simple wrapper class:
and the test :
Output :
Note : If you dont override hashcode() then you will get true only