我有以下代码
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Person {
private String name;
private long birthTime;
@Override
public int hashCode() {
return Objects.hash(name, birthTime);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(name, other.name)
&& birthTime == other.birthTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getBirthTime() {
return birthTime;
}
public void setBirthTime(long birthTime) {
this.birthTime = birthTime;
}
public static Person person(String name, long time) {
Person p = new Person();
p.setName(name);
p.setBirthTime(time);
return p;
}
public static void main(String[] args) {
Map<Person, Person> map = new HashMap<>();
Person p = person("alice", 3);
System.out.println("1. " + map.containsKey(p));
map.put(p, p);
System.out.println("2. " + map.containsKey(p));
p.setName("charlie");
System.out.println("3. " + map.containsKey(p));
Person p2 = person("alice", 3);
System.out.println("4. " + map.containsKey(p2));
Person p3 = person("charlie", 3);
System.out.println("5. " + map.containsKey(p3));
}
}
我期望输出为false、true、true、false和true。但是,输出是false、true、false、false、false。
我正在寻找第三种和第五种情况的输出是如何错误的。HashMap containsKey的行为是什么?
为什么即使Map中有Key对象,输出也是false。对于Person类,equals和hashcode方法都被重写。
以下语句破坏了您的Map:
p.setName("charlie");
这将导致变量p
引用的键不再位于与其hashCode()
匹配的bin中,因为您正在更改其hashCode()
。
如果更改会影响hashCode()
或equals()
的结果,则不应更改Map中已存在的键的状态。
p.setName("charlie");
System.out.println("3. " + map.containsKey(p));
返回CCD_ 6,因为名称为"的CCD_;查理;未映射到与名称为"0"的CCD_ 8实例相同的bin;爱丽丝";。因此CCD_ 9在与名称"0"匹配的bin中搜索CCD_;charlie";,但在那里找不到。
Person p2 = person("alice", 3);
System.out.println("4. " + map.containsKey(p2));
返回false
,因为p2
不等于p
(它们有不同的名称(。
Person p3 = person("charlie", 3);
System.out.println("5. " + map.containsKey(p3));
返回CCD_ 14,因为关键字CCD_;爱丽丝";,即使其当前名称是";charlie";,所以containsKey()
在错误的bin中搜索它,但没有找到它。
在HashMap
中添加对象作为键后,您正在修改该对象,以更改哈希代码。这就像把你的联系方式告诉别人,搬家,然后仍然希望他们能找到你。
当您向映射添加密钥时,它会存储哈希代码。当您尝试查找密钥时,映射会询问您尝试查找的密钥的哈希代码,并有效地查找具有相同存储哈希代码的任何条目。作为";新的";散列码与";旧的";哈希代码,它找不到任何候选代码来与equals
进行检查。
基本上,在使用对象作为映射中的键之后,不应该修改任何影响哈希代码或相等性的内容。
输入有关Eran答案的更多信息。我已经检查了一些HashMap的来源。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
...
tab[i] = newNode(hash, key, value, null);
...
}
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
在第三种情况下;节点";即使你把它的名字改成了";查理";。这就是它返回false的原因。似乎你永远不应该更改对象密钥,因为它会破坏哈希(密钥(不匹配的映射