我的自定义类,将由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
答案 0 :(得分:29)
因为修改p1.hashCode()
时p1
会发生更改,所以无法再在哈希表的原始索引处找到它。永远不要让哈希值依赖于可变字段。
(你很幸运,它在测试期间失败了;它可能也成功了,但只是在生产中失败了。)
答案 1 :(得分:5)
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.
在您的示例中就是这种情况,因为更改name
上的age
或p1
会影响相等比较。所以根据ApiDoc,你的案例中Set的行为是未指定的。
答案 2 :(得分:1)
哈希是键和值的简单配对。以下是在伪代码重命名之前和之后代码状态的显示方式:
<强>之前:强>
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时与其先前的哈希码相关联。
答案 3 :(得分:1)
我认为你需要让hashCode确实依赖于可变字段:当你覆盖取决于可变字段的equals时。
来自hashCode的契约:“如果两个对象根据equals(Object)方法相等,那么在两个对象中的每一个上调用hashCode方法必须产生相同的整数结果。”
因此,如果您创建了两个对象,使得A.equals(B)为真,然后修改A以使A.equals(B)变为false,则需要更改hashCodes。
确实在hashCode的文档中声明“如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须产生不同的整数结果。“,但我不知道这有多大帮助。