我有以下代码
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));
}
}我期望输出为假、真、真、假和真。然而,输出是假的,真的,假的。
我正在寻找如何输出是错误的第3和第5种情况。HashMap containsKey的行为是什么?
为什么输出是假的,即使关键对象在地图中。针对Person类,等于和hashcode方法都被重写。
发布于 2020-07-13 07:01:02
以下语句破坏了您的Map:
p.setName("charlie");它将导致变量p引用的键不再定位在与其hashCode()匹配的bin中,因为您正在更改其hashCode()。
如果更改影响到hashCode()或equals()的结果,则不应该更改已在Map中的键的状态。
p.setName("charlie");
System.out.println("3. " + map.containsKey(p));返回false,因为名为"charlie“的Person实例与名为"alice”的Person实例没有映射到同一个bin。因此,containsKey()在与名称"charlie“匹配的bin中搜索p,但在那里找不到它。
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));返回false,因为键p位于与"alice“名称匹配的bin中,即使它当前的名称是"charlie",所以containsKey()在错误的bin中搜索它,却找不到它。
发布于 2020-07-13 07:02:46
在将对象作为键添加到HashMap中之后,您将以更改哈希代码的方式修改该对象。这就像给某人你的联系方式,搬家,然后仍然期待他们能够找到你。
当您向映射添加一个键时,它会存储哈希代码。当您试图查找密钥时,映射将询问要查找的密钥的哈希代码,并有效地查找具有相同存储的哈希代码的任何条目。由于“新”哈希代码与“旧”哈希代码不匹配,因此无法找到任何可以与equals检查的候选哈希代码。
基本上,在将对象用作映射中的键后,不应该修改任何影响哈希代码或相等性的内容。
发布于 2020-07-13 14:12:50
提供更多关于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;
}在第三种情况下,“节点”中键的哈希值保持不变,即使您将其‘名称’更改为"Charlie“。这就是为什么它返回假的原因。似乎永远不应该更改对象键,因为它会破坏哈希(键)不匹配的映射。
https://stackoverflow.com/questions/62870691
复制相似问题