LinkedHashSet:hashCode()和equals()匹配,但contains()不匹配

时间:2011-11-04 10:17:37

标签: java collections set linkedhashset

以下情况如何:

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行和{{ 1}}两倍长。而且我不认为它是相关的 - 只要它们对于这两个对象是相同的。

3 个答案:

答案 0 :(得分:4)

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

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

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

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。为了遵守规则,对象的hashCode应该相同,所以你也要重新定义hashCode。

编辑:我原本期待hashCodeequals实施错误,但自从您的回答以来,您发现您在将密钥添加到HashSet或HashMap。

将一个Object添加到哈希集合时,会计算其hashCode并将其用于将其映射到Collection中的物理位置。

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

因此,请始终使用HashMap或HashSet键Immutable

答案 1 :(得分:1)

知道了。一旦你知道了,答案是如此明显,你只能尴尬地脸红。

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()的字段!

答案 2 :(得分:0)

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

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 (!!)
}

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

除了第一个输出外,我仍然可以获得“真实”。请更具体地说明“arg”参数的类型。