【问题标题】:LinkedHashSet: hashCode() and equals() match, but contains() doesn'tLinkedHashSet:hashCode() 和 equals() 匹配,但 contains() 不匹配
【发布时间】:2011-12-21 21:43:15
【问题描述】:

以下怎么可能:

void contains(LinkedHashSet data, Object arg) {
    System.out.println(data.getClass()); // java.util.LinkedHashSet
    System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
    System.out.println(arg.equals(data.iterator().next())); // true
    System.out.println(new ArrayList(data).contains(arg)); // true
    System.out.println(new HashSet(data).contains(arg)); // true
    System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
    System.out.println(data.contains(arg)); // false
}

我做错了吗?

显然,它并不总是会发生(如果您创建了一组微不足道的对象,您将不会重现它)。但在我的情况下,它总是会发生更复杂的 arg 类。

编辑:我在这里不定义arg 的主要原因是它的类相当大,Eclipse 生成的hashCode 跨越20 行,equals 长两倍.而且我认为这无关紧要——只要它们对两个对象相等即可。

【问题讨论】:

  • 如果参数是setarg,那么data是什么?你在里面放了什么样的对象?如果是您自己的对象,您是否正确覆盖了equals()hashCode()
  • 另外你为什么在倒数第二行检查contains(file),但在最后一行检查contains(arg)file 是什么?
  • stivlo,咄咄逼人 - 两者都只是复制/粘贴时的拼写错误,已修复。

标签: java collections set linkedhashset


【解决方案1】:

当您构建自己的对象并计划在集合中使用它们时,您应该始终覆盖以下方法:

boolean equals(Object o);
int hashCode();

equals 的默认实现检查对象是否指向同一个对象,而您可能希望重新定义它以检查内容。

在合理的情况下,Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。为了遵守规则,一个对象的 hashCode 等于另一个对象应该是相同的,因此您还必须重新定义 hashCode。

编辑:我期待一个错误的hashCodeequals 实现,但是自从你的回答之后,你透露你在将键添加到HashSet 或HashMap 后对其进行了变异。

当您将对象添加到哈希集合时,会计算其 hashCode 并将其映射到集合中的物理位置。

如果用于计算 hashCode 的某些字段发生变化,hashCode 本身也会发生变化,因此 HashSet 的实现会变得混乱。当它试图获取对象时,它将查看另一个物理位置,并且找不到对象。如果您枚举该集合,则该对象仍将存在。

因此,请始终将 HashMap 或 HashSet 键设为 Immutable

【讨论】:

  • 好吧,我读过,实际上我发现你是数据、集合和 arg,而你的意思只是数据和 arg。要获得进一步帮助,您应该展示您的 arg 类以及如何实现 equals 和 hashCode,但由于您的评论很刻薄,请不要指望我的帮助。
  • arg/data 差异是一个很好的发现,但我这边只是复制/粘贴疏忽。我的严厉反应是因为从描述中很明显我知道 hashCode 和 equals,并且它们在这种情况下正常工作。
  • 好的,那么让我们冷静一下。我认为没有看到对象是不可能回答这个问题的。
  • 如果类重写了 equals 方法,并且 hashCode 契约要求相等的对象具有相等的哈希码。
【解决方案2】:

知道了。知道了答案就很明显了,只能尴尬的脸红。

static class MyObj {
    String s = "";

    @Override
    public int hashCode() {
        return s.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return ((MyObj) obj).s.equals(s);
    }
}

public static void main(String[] args) {
    LinkedHashSet set = new LinkedHashSet();
    MyObj obj = new MyObj();
    set.add(obj);
    obj.s = "a-ha!";
    contains(set, obj);
}

这足以可靠地复制它。

解释:你永远不要改变用于 hashCode() 的字段!

【讨论】:

    【解决方案3】:

    您的问题似乎缺少某些内容。我做了一些猜测:

    private void testContains() {
      LinkedHashSet set = new LinkedHashSet();
      String hello = "Hello!";
      set.add(hello);
      contains(set, hello);
    }
    
    void contains(LinkedHashSet data, Object arg) {
      System.out.println(data.getClass()); // java.util.LinkedHashSet
      System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
      System.out.println(arg.equals(data.iterator().next())); // true
      System.out.println(new ArrayList(data).contains(arg)); // true
      System.out.println(new HashSet(data).contains(arg)); // true
      System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
      System.out.println(data.contains(arg)); // true (!!)
    }
    

    已编辑:跟踪不断变化的问题!

    除了第一个输出之外,我仍然得到“真实”的 ALL。请更具体地了解“arg”参数的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-01
      • 2014-05-03
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多