【问题标题】:Strange Java HashMap behavior - can't find matching object奇怪的 Java HashMap 行为 - 找不到匹配的对象
【发布时间】:2010-07-28 20:35:54
【问题描述】:

我在尝试在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;
}

那个奇怪的 for 循环之所以存在,是因为对于一个特定的执行,我碰巧知道data此时只包含一个项目,它是k1,实际上是bool2,@ 987654326@ 和 bool4 在该执行中将被评估为 true。但是,bool1 将被评估为false,而v 将为空。

现在,这是一个更大程序的一部分——我无法在更小的样本上重现错误——但在我看来,无论程序的其余部分做什么,这种行为都不应该发生。

编辑:我已手动验证哈希码在对象插入地图和查询它的时间之间没有发生变化。我会继续检查这个场地,但还有其他选择吗?

【问题讨论】:

    标签: java hashmap


    【解决方案1】:

    如果密钥的哈希码在插入地图后发生更改,则可能会发生这种情况。

    这是您描述的行为的示例:

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

    【讨论】:

    • 或者equals()中使用的字段被改变了。
    【解决方案2】:

    来自Map接口的API描述:

    注意:如果出现以下情况,必须非常小心 可变对象用作映射键。 未指定地图的行为 如果一个对象的值改变了 以影响平等的方式 对象是键时的比较 在地图中。这种情况的一个特例 禁止是它不是 允许地图包含 本身作为一把钥匙。虽然它是 允许地图包含 本身作为一个价值,极端谨慎是 建议:equals 和 hashCode 方法不再被很好地定义在 这样的地图。

    此外,对于用作 Map 键的类型,equals() 和 hashCode() 的行为也有非常具体的要求。不遵守这里的规则会导致各种未定义的行为。

    【讨论】:

      【解决方案3】:

      如果您确定哈希码在插入密钥和执行包含检查之间没有变化,那么某处存在严重错误。您确定您使用的是java.util.HashMap 而不是某种子类吗?你知道你使用的JVM是什么实现吗?

      这是来自 Sun 的 1.6.0_20 JVM 的 java.util.HashMap.getEntry(Object key) 的源代码:

      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;
      

      如您所见,它检索hashCode,转到表中的相应槽,然后对该槽中的每个元素进行equals 检查。如果这是您正在运行的代码并且密钥的哈希码没有改变,那么它一定是在进行相等检查,这一定是失败的。

      下一步是让您向我们提供更多代码或上下文 - 至少是 Key 类的 hashCodeequals 方法。

      或者,如果可以的话,我会建议连接到调试器。观察您的密钥散列到哪个存储桶,并逐步检查 containsKey 以查看失败的位置。

      【讨论】:

      • 看水桶的好建议。
      【解决方案4】:

      这个应用程序是多线程的吗?如果是这样,另一个线程可以更改data.containsKey(k1) 调用和data.keySet() 调用之间的数据。

      【讨论】:

        【解决方案5】:

        如果 equals() 对两个对象返回 true,则 hashCode() 应该返回相同的值。如果 equals() 返回 false,则 hashCode() 应该返回不同的值。 供参考:

        http://www.ibm.com/developerworks/java/library/j-jtp05273.html

        【讨论】:

          【解决方案6】:

          也许 Key 类看起来像

          Key
          {
              boolean equals = false ;
          
              public boolean equals ( Object oth )
              {
                   try
                   {
                        return ( equals ) ;
                   }
                   finally
                   {
                        equals = true ;
                   }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-12
            • 2011-12-26
            相关资源
            最近更新 更多