gpt4 book ai didi

java - 为什么 Map 的 containsKey() 只调用 hashCode()?

转载 作者:搜寻专家 更新时间:2023-11-01 01:26:17 24 4
gpt4 key购买 nike

我读了 interface Map 的 JavaDoc,它是这样说的:

Many methods in Collections Framework interfaces are defined in terms of the equals method. For example, the specification for the containsKey(Object key) method says: "returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k))." This specification should not be construed to imply that invoking Map.containsKey with a non-null argument key will cause key.equals(k) to be invoked for any key k. Implementations are free to implement optimizations whereby the equals invocation is avoided, for example, by first comparing the hash codes of the two keys. (The Object.hashCode() specification guarantees that two objects with unequal hash codes cannot be equal.)

我的理解是当调用containsKey()时,hashCode()和equals()都会被调用,所以我自己写了代码来测试。

HappyDay 类将作为键存储在 HashMap 中,我重写 hashCode() 和 equals() 方法,并添加 System.out.println("invoking hashCode()"+ this.happyHour);System.out.println("invoking equals()"); 检查方法是否被调用。

public class HappyDay {

private final int happyHour;

public HappyDay(int hh) {
this.happyHour = hh;
}

public int getHappyHour() {
return this.happyHour;
}

@Override
public boolean equals(Object o) {
System.out.println("invoking equals()");
if (o == null) {return false;}

if (o == this) {return true;}

//this is an easy overridden, if happy hour equal, objects will be equal.
if (o instanceof HappyDay) {
HappyDay other = (HappyDay) o;
int otherHappyHour = other.getHappyHour();
if (this.happyHour == otherHappyHour) {
return true;
}
}
return false;
}

@Override
public int hashCode() {
System.out.println("invoking hashCode()" + this.happyHour);
int hash = 7;
hash = hash + this.happyHour;
return hash;
}
}

public class Main {
public static void main(String[] args) {
Map<HappyDay,String> hm = new HashMap<>();

HappyDay hd1 = new HappyDay(1);
HappyDay hd2 = new HappyDay(2);

hm.put(hd1, "hd1");
hm.put(hd2, "hd2");

if(hm.containsKey(hd2)){
System.out.println("found");
}else{
System.out.println("not exist");
}
}
}

Main类是将两个HappyDay实例放入HashMap中,插入后(put()方法),调用hm.containsKey(hd2),引用JavaDoc,应该调用hashCode () 先调用equals(),但是输出是

invoking hashCode()1 //call put()
invoking hashCode()2 //call put()
invoking hashCode()2 //call containsKey()
found

我预计还有另一条输出线应该是 invoking equals(),谁能帮我解释一下为什么没有调用 equals()?

最佳答案

HashMap 首先通过==检查键是否相等; 只有当失败时,它才会继续检查equals

现在,您将 hd2 作为键放入 map 中,然后使用 相同的对象 作为参数检查 containsKey,所以== 测试通过并且永远不会调用 equals

检查 map 是否包含给定键归结为检查 getEntry(key) 是否返回 null。让我们look at the source :

360     final Entry<K,V> getEntry(Object key) {
361 int hash = (key == null) ? 0 : hash(key.hashCode());
362 for (Entry<K,V> e = table[indexFor(hash, table.length)];
363 e != null;
364 e = e.next) {
365 Object k;
366 if (e.hash == hash &&
367 ((k = e.key) == key || (key != null && key.equals(k))))
368 return e;
369 }
370 return null;
371 }

在第 367 行,我们可以看到 == 测试在 equals 测试之前执行。 short-circuiting如果 == 通过,|| 将完全跳过 equals 测试,这就是这里发生的情况。

这可能是因为它跳过了潜在的昂贵的 equals 方法(例如 String#equals 必须检查给定字符串的每个字符)。 equals contract还声明如果 o1 == o2o1.equals(o2) 应该为真,因此这是一个有效的优化。


让我们通过稍微修改您的代码来进行健全性检查:

if (hm.containsKey(new HappyDay(2))) {
System.out.println("found");
} else {
System.out.println("not exist");
}

现在的输出是:

invoking hashCode()1invoking hashCode()2invoking hashCode()2invoking equals()found

注意 equals 被调用了。这是有道理的,因为我们正在用一个但相等的对象调用containsKey,所以== 测试返回false 而equals 执行测试。

关于java - 为什么 Map 的 containsKey() 只调用 hashCode()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23817269/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com