Java HashMap containsKey

Java HashMap containsKey

我有以下代码

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,false,false,false.

我正在寻找第 3 和第 5 种情况的输出如何为假。 HashMap containsKey 的行为是什么?

为什么即使 Key 对象存在于 Map 中,输出仍为 false。 Person class.

的 equals 和 hashcode 方法都被覆盖了

以下语句破坏了您的地图:

p.setName("charlie");

它导致变量 p 引用的键不再位于与其 hashCode() 匹配的容器中,因为您正在更改其 hashCode().

如果更改影响 hashCode()equals().

的结果,则永远不要更改已在 Map 中的键的状态
p.setName("charlie");
System.out.println("3. " + map.containsKey(p));

Returns false 因为名称为“charlie”的 Person 实例未映射到与名称为“alice”的 Person 实例相同的 bin .因此 containsKey() 在与名称“charlie”匹配的容器中搜索 p,但没有找到它。

Person p2 = person("alice", 3);
System.out.println("4. " + map.containsKey(p2));

Returns false 因为 p2 不等于 p (他们有不同的名字)。

Person p3 = person("charlie", 3);
System.out.println("5. " + map.containsKey(p3));

Returns false 由于密钥 p 位于与名称“alice”匹配的 bin 中,即使它的当前名称是“charlie”,所以 containsKey() 在错误的 bin 中搜索它,但没有找到它。

您在将对象添加为 HashMap 中的键后对其进行修改,更改散列码的方式。这就像给别人你的联系方式,搬家,然后仍然希望他们能够找到你。

当您向地图添加键时,它会存储哈希码。当您尝试 查找 键时,地图会询问您要查找的键的散列码,并有效地找到具有相同存储散列码的任何条目。由于“新”散列码与“旧”散列码不匹配,因此无法找到任何候选人来检查equals

基本上,在将对象用作映射中的键后,您不应修改任何影响哈希码或相等性的内容。

添加更多关于 Eran answer 的信息。我查看了一些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;
}

在第 3 种情况下,即使您将其名称更改为“查理”,“节点”中键的哈希值也保持不变。这就是它 return 错误的原因。似乎你永远不应该更改对象键,因为它会因 hash(key)

的不匹配而破坏映射