奇怪的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()中使用的字段已更改。有关查看存储桶的好提示。