HashSet包含自定义对象的问题

我的自定义类将由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 personSet = new HashSet(); 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的行为未指定。

我认为你需要让hashCode确实依赖于可变字段:当你覆盖等于依赖于可变字段的equals时。

来自hashCode的契约:“如果两个对象根据equals(Object)方法相等,那么在两个对象中的每一个上调用hashCode方法必须产生相同的整数结果。”

因此,如果你创建两个对象,使得A.equals(B)为真,然后修改A使得A.equals(B)变为false,那么你需要改变hashCodes。

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

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

之前:

 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时与其先前的哈希码相关联。