我有一个用于存储对象的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
是什么导致了这种奇怪的行为?
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;
}
}
发布于 2014-02-06 11:01:20
在将键插入地图后,您不应修改它。
编辑:我在地图中找到了javadoc的摘录:
注意:如果将可变对象用作映射键,则必须非常小心。如果对象的值以影响等于比较的方式更改,而对象是映射中的键,则不指定映射的行为。
示例中有一个简单的包装类:
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
发布于 2014-02-06 11:06:18
正如阿诺德·德诺埃尔所指出的那样,修改密钥可以产生这种效果。原因是containsKey
关心散列图中密钥的桶,而迭代器不关心。如果映射中的第一个键--不考虑桶--恰好是你想要的那个,那么你就可以得到你看到的行为。如果地图中只有一个条目,这当然是有保证的。
想象一张简单的两桶地图:
[0: empty] [1: yourKeyValue]
迭代器如下所示:
yourKeyValue
然而,containsKey
方法如下所示:
keyToFind
有一个hashCode() == 0
,所以让我看看桶0(只在那里)。哦,它是空的-返回false.
事实上,即使钥匙留在同一个桶里,你也会遇到这个问题!如果您查看HashMap
的实现,您将看到每个键值对都与密钥的哈希代码一起存储。当映射想要检查存储的密钥和传入的密钥时,它使用equals
((k = e.key) == key || (key != null && key.equals(k))))
这是一个很好的优化,因为这意味着具有不同hashCodes的键碰巧碰撞到同一个桶中,这将被看作是非常便宜的不相等的(只是int
比较)。但这也意味着更改密钥--这不会改变存储的e.key
字段--将破坏映射。
发布于 2014-06-03 22:15:48
调试java源代码时,我意识到containsKey方法针对键集中的每个元素检查搜索密钥上的两件事:、hashCode、和等于;它按照这个顺序进行检查。
这意味着,如果是obj1.hashCode() != obj2.hashCode()
,则返回false (而不计算obj1.equals(obj2) )。但是,如果是obj1.hashCode() == obj2.hashCode()
,则返回obj1.equals(obj2)
您必须确保这两种方法都是-may,您必须重写它们--对于您定义的标准,计算值为true。
https://stackoverflow.com/questions/21600344
复制相似问题