假设我有一个人类,并且相等性基于id
属性。以下是Person
class -
class Person {
private int id;
private String firstName;
public Person(int id, String firstName) {
super();
this.id = id;
this.firstName = firstName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public int hashCode() {
return this.id;
}
public boolean equals(Object obj) {
return ((Person) obj).getId() == this.id;
}
}
我使用Person
类作为HashMap的键。现在看下面的代码 -
import java.util.HashMap;
public class TestReport {
public static void main(String[] args) {
Person person1 = new Person(1, "Person 1");
Person person2 = new Person(2, "Person 2");
HashMap<Person, String> testMap = new HashMap<Person, String>();
testMap.put(person1, "Person 1");
testMap.put(person2, "Person 2");
person1.setId(2);
System.out.println(testMap.get(person1));
System.out.println(testMap.get(person2));
}
}
注意,虽然我们已经添加了两个不同的person对象作为HashMap的键,但后来我们将id
person1
对象更改为2以使person对象相等。
现在,我的输出为 -
Person 2
Person 2
我可以看到HashMap中有两个键值对,其中包含数据:“person1 / Person 1”和“person2 / Person 2”,我仍然总是将“Person 2”作为输出,我永远无法访问值“人1”。另请注意,我们在HashMap中有重复键。
在查看源代码后我可以理解这种行为,但它似乎不是问题吗?我们可以采取一些预防措施来阻止它吗?
答案 0 :(得分:1)
这种情况正在发生,因为您的equals()
方法仅使用哈希码。您应该比较所有人员字段,例如firstName
。
答案 1 :(得分:1)
这完全取决于hashCode()
使用HashMap
值的方式。
虽然需要两个相同哈希码的相等对象,但反向不一定正确。两个不相等的对象可以具有相同的哈希码(因为int
只有有限的可能值集)。
每次将对象放入HashMap
时,它都会将对象存储在由密钥hashCode()
标识的存储桶中。因此,根据hashCode()
的实现方式,您应该在各种存储桶中公平分配条目。
现在,当您尝试检索值时,HashMap
将识别给定key
所属的存储桶,然后将遍历该存储桶中的所有密钥以选择给定密钥的条目 - 在此阶段,它将使用equals()
方法来识别条目。
在您的情况下,person1
位于bucket 1
,person2
位于bucket 2
。
但是,当您通过更新hashCode()
更改了person1
的{{1}}值时,id
并未意识到此更改。稍后,当您使用HashMap
作为密钥查找条目时,person1
认为它应该出现在HashMap
中(因为bucket 2
现在是person1.hashCode()
) ,之后当它使用2
方法迭代bucket 2
时,它会找到equals
的条目,并认为它是您感兴趣的对象person2
case也基于equals
属性。
如果查看implementation of HashMap#get
方法,上面的说明很明显,如下所示:
id
PS 有时当您知道问题的答案时,您忘记查找重复的问题,并在任何人都可以回复之前直接回答问题 - 这就是本案中发生的事情。下次要小心: - )