奇怪的Java HashMap行为-can';找不到匹配的对象
在奇怪的Java HashMap行为-can';找不到匹配的对象,java,hashmap,Java,Hashmap,在java.util.HashMap中查找密钥时,我遇到了一些奇怪的行为,我想我遗漏了一些东西。代码段基本上是: HashMap<Key, Value> data = ... Key k1 = ... Value v = data.get(k1); boolean bool1 = data.containsKey(k1); for (Key k2 : data.keySet()) { boolean bool2 = k1.equals(k2); boolean bo
java.util.HashMap
中查找密钥时,我遇到了一些奇怪的行为,我想我遗漏了一些东西。代码段基本上是:
HashMap<Key, Value> data = ...
Key k1 = ...
Value v = data.get(k1);
boolean bool1 = data.containsKey(k1);
for (Key k2 : data.keySet()) {
boolean bool2 = k1.equals(k2);
boolean bool3 = k2.equals(k1);
boolean bool4 = k1.hashCode() == k2.hashCode();
break;
}
HashMap数据=。。。
键k1=。。。
值v=数据获取(k1);
布尔bool1=data.containsKey(k1);
for(键k2:data.keySet()){
布尔bool2=k1,等于(k2);
布尔bool3=k2.equals(k1);
布尔bool4=k1.hashCode()==k2.hashCode();
打破
}
这种奇怪的for循环之所以存在,是因为对于特定执行我碰巧知道数据
此时只包含一项,它是k1
,实际上bool2
,bool3
和bool4
将在该执行中计算为true
<但是,code>bool1将被计算为false
,而v
将为空
现在,这是一个更大程序的一部分-我无法在更小的样本上重现错误-但在我看来,无论程序的其余部分做什么,这种行为永远不会发生
编辑:我已手动验证哈希代码在对象插入到映射和查询对象之间没有变化。我会继续检查这个地点,但还有其他选择吗?如果密钥的哈希代码在插入地图后发生更改,则可能会发生此行为 以下是您描述的行为示例:
public class Key
{
int hashCode = 0;
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
return hashCode == other.hashCode;
}
public static void main(String[] args) throws Exception {
HashMap<Key, Integer> data = new HashMap<Key, Integer>();
Key k1 = new Key();
data.put(k1, 1);
k1.hashCode = 1;
boolean bool1 = data.containsKey(k1);
for (Key k2 : data.keySet()) {
boolean bool2 = k1.equals(k2);
boolean bool3 = k2.equals(k1);
boolean bool4 = k1.hashCode() == k2.hashCode();
System.out.println("bool1: " + bool1);
System.out.println("bool2: " + bool2);
System.out.println("bool3: " + bool3);
System.out.println("bool4: " + bool4);
break;
}
}
}
公共类密钥
{
int hashCode=0;
@凌驾
公共int hashCode(){
返回哈希码;
}
@凌驾
公共布尔等于(对象obj){
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
键其他=(键)obj;
返回hashCode==other.hashCode;
}
公共静态void main(字符串[]args)引发异常{
HashMap数据=新的HashMap();
键k1=新键();
数据置位(k1,1);
k1.hashCode=1;
布尔bool1=data.containsKey(k1);
for(键k2:data.keySet()){
布尔bool2=k1,等于(k2);
布尔bool3=k2.equals(k1);
布尔bool4=k1.hashCode()==k2.hashCode();
System.out.println(“bool1:+bool1”);
System.out.println(“bool2:+bool2”);
System.out.println(“bool3:+bool3”);
System.out.println(“bool4:+bool4”);
打破
}
}
}
此应用程序是多线程的吗?如果是这样,另一个线程可以更改data.containsKey(k1)
调用和data.keySet()调用之间的数据。来自映射接口的API描述:
注意:如果发生以下情况,必须非常小心
可变对象用作贴图关键点。
未指定映射的行为
如果对象的值已更改
以影响平等的方式
对象是键时的比较
在地图上。这是一个特例
禁止是因为它不是
允许地图包含
它本身就是一把钥匙。尽管如此
允许地图包含
作为一种价值观,极端谨慎是必要的
建议:equals和hashCode
方法不再在上定义良好
这样的地图
此外,对于用作映射键的类型,equals()和hashCode()的行为有非常具体的要求。如果不遵守此处的规则,将导致各种未定义的行为。如果equals()对两个对象返回true,则hashCode()应返回相同的值。如果equals()返回false,则hashCode()应返回不同的值。
供参考:
也许关键类看起来像
Key
{
boolean equals = false ;
public boolean equals ( Object oth )
{
try
{
return ( equals ) ;
}
finally
{
equals = true ;
}
}
}
如果您确定哈希代码在插入密钥和执行包含检查之间没有变化,那么某个地方就出了严重的问题。您确定使用的是java.util.HashMap
而不是某种子类吗?您知道您正在使用的JVM实现是什么吗
以下是Sun 1.6.0_20 JVM中java.util.HashMap.getEntry(对象键)
的源代码:
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
final Entry getEntry(对象键){
int hash=(key==null)?0:hash(key.hashCode());
for(条目e=表[indexFor(hash,table.length)];
e!=null;
e=e.next){
对象k;
如果(e.hash==hash&&
((k=e.key)==key | |(key!=null&&key.equals(k)))
返回e;
}
返回null;
如您所见,它检索hashCode
,转到表中相应的插槽,然后对该插槽中的每个元素执行equals
检查。如果这是您正在运行的代码,并且密钥的哈希代码没有更改,则它必须执行equals检查,该检查必须失败
下一步是为我们提供更多的代码或上下文-您的密钥类的hashCode
和至少等于方法
或者,如果可以的话,我建议您连接到调试器。查看您的密钥散列到哪个存储桶,并逐步执行containsKey检查以查看失败的位置。或者equals()中使用的字段已更改。有关查看存储桶的好提示。