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

发布于 2024-12-13 16:04:18 字数 818 浏览 1 评论 0原文

以下怎么可能:

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 行和等于两倍长。我认为这无关紧要——只要它们对于两个对象来说是相等的。

How is the following possible:

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
}

Am I doing something wrong?

Obviously, it doesn't always happen (if you create a trivial set of Objects, you won't reproduce it). But it does always happen in my case with more complicated class of arg.

EDIT: The main reason why I don't define arg here is that's it's fairly big class, with Eclipse-generated hashCode that spans 20 lines and equals twice as long. And I don't think it's relevant - as long as they're equal for the two objects.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北音执念 2024-12-20 16:04:18

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

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

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

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

编辑:我本来期待一个错误的hashCodeequals实现,但自从你的回答以来,你透露你在修改密钥后添加到 HashSet 或 HashMap。

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

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

因此,请始终使 HashMap 或 HashSet 键不可变< /a>.

When you build your own objects, and plan to use them in a collection you should always override the following methods:

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

The default implementation of equals checks whether the objects point to the same object, while you'd probably want to redefine it to check the contents.

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. To respect the rules an hashCode of an object equals to another one should be the same, thus you've also to redefine hashCode.

EDIT: I was expecting a faulty hashCode or equals implementation, but since your answer, you revealed that you're mutating the keys after they are added to an HashSet or HashMap.

When you add an Object to an hash collection, its hashCode is computed and used to map it to a physical location in the Collection.

If some fields used to compute the hashCode are changed, the hashCode itself will change, so the HashSet implementation will become confused. When it tries to get the Object it will look at another physical location, and won't find the Object. The Object will still be present if you enumerate the set though.

For this reason, always make HashMap or HashSet keys Immutable.

治碍 2024-12-20 16:04:18

知道了。一旦你知道了,答案就显而易见了,你只能尴尬地脸红。

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

Got it. Once you know it, the answer is so obvious you can only blush in embarrassment.

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

That is enough to reliably reproduce it.

Explanation: Thou Shalt Never Mutate Fields Used For hashCode()!

狼性发作 2024-12-20 16:04:18

你的问题似乎缺少一些东西。我做了一些猜测:

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

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

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

There seems to be something missing from your question. I have made some guesses:

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

EDITED: To keep track of changing question!

I still get "true" for ALL but the first output. Please be more specific about the type of the "arg" parameter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文