HashSet 包含自定义对象的问题

发布于 2024-10-19 03:16:04 字数 1682 浏览 1 评论 0原文

我的自定义类将包含在 HashSet 中

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "hashcode='" + this.hashCode() + '\'' +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (!name.equals(person.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

我的 HashSet 测试失败了

   public void hashSetTest() {
        Set<Person>  personSet = new HashSet<Person>();
        Person p1 = new Person("raghu", 12);
        Person p2 = new Person("rimmu", 21);

        personSet.add(p1);
        personSet.add(p2);


       p1.setName("raghus");
       p1.setAge(13);

       int i2 =p1.hashCode();
       System.out.println(personSet.size() + ": "+ p1.hashCode()+" : "+personSet.contains(p1)+ " : "+i2);
    }

我期望 personSet.contains(p1) 通过。为什么会返回 false? 谢谢 斯里

My Custom class that will be contained by HashSet

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "hashcode='" + this.hashCode() + '\'' +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (!name.equals(person.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

My HashSet test that fails

   public void hashSetTest() {
        Set<Person>  personSet = new HashSet<Person>();
        Person p1 = new Person("raghu", 12);
        Person p2 = new Person("rimmu", 21);

        personSet.add(p1);
        personSet.add(p2);


       p1.setName("raghus");
       p1.setAge(13);

       int i2 =p1.hashCode();
       System.out.println(personSet.size() + ": "+ p1.hashCode()+" : "+personSet.contains(p1)+ " : "+i2);
    }

Iam expecting personSet.contains(p1) to pass. Why is it returning false?
Thanks
sri

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

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

发布评论

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

评论(4

风追烟花雨 2024-10-26 03:16:04

因为当您修改 p1 时,p1.hashCode() 会发生变化,因此无法再在哈希表中的原始索引中找到它。永远不要让哈希值依赖于可变字段。

(你很幸运它在测试期间失败了;它也可能成功了,只是在生产中失败了。)

Because p1.hashCode() changes when you modify p1, so it can't be found at its original index in the hash table anymore. Never let a hash value depend on a mutable field.

(You're quite lucky that it fails during testing; it might just as well have succeeded, only to fail in production.)

中二柚 2024-10-26 03:16:04

HashSet 实现 Set。 ApiDoc 指定:

注意:如果将可变对象用作集合元素,则必须格外小心。如果对象的值以影响等于比较的方式更改,而该对象是集合中的元素,则不会指定集合的​​行为。

在您的示例中就是这种情况,因为更改 p1 上的 nameage 会影响相等比较。因此,根据 ApiDoc,您的情况下 Set 的行为未指定。

HashSet implements Set. The ApiDoc specifies:

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

In your example this is the case, because changing name or age on p1 affects equal-comparison. So according the ApiDoc, the behavior of Set in your case is unspecified.

感性 2024-10-26 03:16:04

我认为你需要让 hashCode 经常依赖于可变字段:当你重写 equals 时,它依赖于可变字段。

根据 hashCode 的约定:“如果根据 equals(Object) 方法,两个对象相等,则对这两个对象调用 hashCode 方法必须产生相同的整数结果。”

因此,如果您创建两个对象使得 A.equals(B) 为 true,然后修改 A 以使 A.equals(B) 变为 false,则您也需要更改 hashCodes。

确实,hashCode 的文档中指出,“如果两个对象根据 equals(java.lang.Object) 方法不相等,则不要求对这两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。 ”,但我不知道这有什么帮助。

I think you need to have hashCode depends on mutable fields quite often indeed: when you override equals that depends on mutable fields.

From hashCode's contract: "If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result."

So, if you create two objects such that A.equals(B) is true, and then modify A such a way that you get A.equals(B) became false, you need to have hashCodes change too.

It's true that in hashCode's documentation is stated that "It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.", but I don't know how this can help.

南烟 2024-10-26 03:16:04

哈希是键和值的简单配对。以下是伪代码重命名之前和之后代码的状态:

之前:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 12),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

p1.setName("raghus"); #p1.hashcode() = SOME_NEW_NUM
p1.setAge(13);#p1.hashcode() = SOME_OTHER_NEW_NUM

之后:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 13),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

由于您可以直接访问 HashSet 中的 p1 对象已正确更新,但 HashSet 不关注所包含对象的哈希码正在更新。当调用 personSet.contains(p1) 时,HashSet 会查找具有新值 <​​code>p1.hashcode() 的条目。

p1 对象在添加到 HashSet 时与其之前的哈希码相关联。

Hashes are simple pairing of key and values. Here's how the state of your code would look before and after the renaming in pseudo-code:

Before:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 12),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

p1.setName("raghus"); #p1.hashcode() = SOME_NEW_NUM
p1.setAge(13);#p1.hashcode() = SOME_OTHER_NEW_NUM

After:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 13),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

Since you have direct access to the p1 the object within the HashSet is updated correctly, but HashSet does not pay attention to contained objects hashcodes being updated. When a call to personSet.contains(p1) is called, the HashSet looks for an entry with the new value of p1.hashcode().

The p1 object is associated with its previous hashcode at the time when it was added to the HashSet.

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