1

以下如何可能:

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两倍。而且我认为这无关紧要-只要它们对两个对象相等即可。

4

3 回答 3

4

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

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

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

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

编辑:我期待一个错误hashCodeequals实现,但是自从你的回答之后,你透露你在将键添加到 HashSet 或 HashMap 后正在改变它们。

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

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

因此,请始终使 HashMap 或 HashSet 键不可变

于 2011-11-04T10:42:21.317 回答
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() 的字段!

于 2011-11-04T11:18:34.253 回答
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 (!!)
}

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

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

于 2011-11-04T10:31:59.780 回答