Search code examples
javahashmapcontainskey

Java HashMap containsKey returns false for existing object


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


}

Solution

  • You shall not modify the key after having inserted it in the map.

    Edit : I found the extract of javadoc in Map :

    Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.

    Example with a simple wrapper class:

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

    and the test :

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

    Output :

    true
    false
    

    Note : If you dont override hashcode() then you will get true only