【问题标题】:Java HashMap containsKey [duplicate]Java HashMap containsKey [重复]
【发布时间】:2020-07-13 06:57:22
【问题描述】:

我有以下代码

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 的行为是什么?

为什么即使 Key 对象存在于 Map 中,输出仍为 false。 Person 类的 equals 和 hashcode 方法都被覆盖了。

【问题讨论】:

  • “不得不添加很多多余的细节,因为内容小于代码,所以不允许我这样做。”除此之外,我在这里没有看到任何多余的东西,如果您在没有文本的情况下提供了代码,那么这个问题将很难理解。虽然您的代码可以通过基本上只进行 一个 检查(或者可能一个具有预期结果,一个具有意外结果)而不是五个来缩短。
  • 由于这个原因,在 HashMaps 中用作键的类应该是不可变的

标签: java hashmap containskey


【解决方案1】:

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

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 中搜索它,并且不会没找到。

【讨论】:

  • 啊。谢谢你。我错过了这样一个事实,即 hashCode 现在将解析为不同的存储桶,因为 Key 值已更改。
  • @TechEnthusiast:它是否解析为不同的存储桶并不重要——我当然期望地图检查相等的哈希码(存储和“密钥我们正在寻找”),然后再致电equals。即使只有一个桶,如果哈希码不同,也不会被找到。
【解决方案2】:

在将对象添加为HashMap 中的键之后,您正在修改该对象,以更改哈希码的方式。这就像给某人您的联系方式,搬家,然后仍然期望他们能够找到您。

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

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

【讨论】:

    【解决方案3】:

    把更多关于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;
    }
    

    在第三种情况下,“节点”中键的哈希值保持不变,即使您将其名称更改为“查理”。这就是它返回假的原因。鉴于 hash(key) 不匹配会破坏映射,您似乎永远不应该更改对象键

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-17
      • 1970-01-01
      • 2020-11-30
      • 2013-03-04
      相关资源
      最近更新 更多