簡體   English   中英

HashSet包含自定義對象的問題

[英]HashSet contains problem with custom objects

我的自定義類將由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)能夠通過。 為什么它會返回假? 謝謝sri

因為p1.hashCode()在修改p1時會發生更改,所以無法再在哈希表中的原始索引處找到它。 永遠不要讓哈希值依賴於可變字段。

(你很幸運,它在測試期間失敗了;它可能也成功了,只是在生產中失敗了。)

HashSet實現了Set ApiDoc規定:

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.

在您的示例中就是這種情況,因為在p1上更改nameage會影響相等比較。 因此,根據ApiDoc,您的案例中Set的行為未指定。

哈希是鍵和值的簡單配對。 以下是在偽代碼重命名之前和之后代碼的狀態:

之前:

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

由於您可以直接訪問p1,因此HashSet中的對象可以正確更新,但HashSet不會注意要更新的包含對象哈希碼。 當調用personSet.contains(p1) ,HashSet會查找具有新值p1.hashcode()的條目。

p1對象在添加到HashSet時與其先前的哈希碼相關聯。

我認為你需要讓hashCode確實依賴於可變字段:當你覆蓋等於依賴於可變字段的equals時。

來自hashCode的契約:“如果兩個對象根據equals(Object)方法相等,那么在兩個對象中的每一個上調用hashCode方法必須產生相同的整數結果。”

因此,如果您創建了兩個對象,使得A.equals(B)為真,然后修改A以使A.equals(B)變為false,則需要更改hashCodes。

確實,在hashCode的文檔中聲明“如果兩個對象根據equals(java.lang.Object)方法不相等,則不需要在兩個對象中的每一個上調用hashCode方法必須產生不同的整數結果。 “,但我不知道這有多大幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM