我作为密钥存储在HashMap中的对象会覆盖equals()但不会覆盖hashCode();当我在地图中放置一个对象时,不会调用equals()方法。如果我也覆盖hashCode(),则调用equals()方法。这是为什么?
为什么我无法使用自定义equals方法,可能会阻止向地图添加对象,无论我是否覆盖hashCode()?
感谢。
package package1;
public class PlayWithHash {
public static void main(String [] args){
java.util.Map<Cat, Cat> map = new java.util.HashMap<Cat, Cat>();
map.put(new Cat(), new Cat());
map.put(new Cat(), new Cat());
System.out.println("hashmap size = " + map.size());
}
}
class Cat{
@Override
public int hashCode(){
return 1;
}
@Override
public boolean equals(Object d){
Cat c = (Cat) d;
if(this.hashCode() == c.hashCode()){
return true;
}
return false;
}
}
如果注释了hashCode(),则size = 2,否则size = 1.
我想如果我不覆盖hashCode()而使用Object的hashCode(),HashMap将“看到”我的两个对象的hashCodes不同,并得出结论没有必要调用equals()。如果这是真的,HashMap正在执行合同中的至少一部分。而且,这可能是HashMap优化的另一种表现形式。
但是,你(greenSocksRock)写道,“...你的密钥的hashCode()实现没有为不同的密钥返回足够不同的哈希值。”你能详细说明吗?
感谢。
PS对不起,我还在试图找出StackOverflow编辑器的方法。
答案 0 :(得分:3)
编译器和JVM都没有强制执行hashCode / equals协定。
合同在Object
和您的班级之间,即您有责任遵守和执行该合同!
在Java中,Object
类列出了.equals()
和.hashCode()
的契约,这是大多数基于哈希的标准库类所依赖的。现在,如果您实现自己的类,它会隐式扩展Object
,因此您应该在自己的实现中强制执行常规合同,如果您希望您的类与依赖该合同的其他类一起正常工作。 / p>
提醒一下,来自Object
documentation:
如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须产生相同的整数结果。
在没有看到您的实现的情况下,我只能猜测为什么在将对象放入equals()
时调用Map
,我的猜测是hashCode()
的{{1}}实现您的密钥没有为不同的密钥返回足够不同的哈希值
例如,当您将某些内容放入HashMap
时,它会调用密钥的hashCode()
方法,以找到存储您的值的位置。如果该点已包含值,则可能会调用键的equals()
方法来确定下一步操作。
我不确定我是否理解你在上一个问题中尝试做什么,当你说你想要防止在地图上添加一个对象时#34;。如果你愿意的话,那就是 肯定能够用自定义equals
实现的东西。在将新条目添加到地图之前,请根据您的具体情况查看Map.containsKey()
或Map.containsValue()
。
最后,如果可以的话,我建议您查看Joshua Bloch的"Effective Java, 2nd ed."中的第8和第9项,以获得更深入的解释。
答案 1 :(得分:0)
AFAIK编译器不会强制执行它。 阅读本文以便更好地理解该主题 http://tutorials.jenkov.com/java-collections/hashcode-equals.html
答案 2 :(得分:0)
散列映射在内部实现为链接列表的集合。搜索对象(使用传递的键)的常用机制是首先对键进行散列,然后遍历链接列表中与散列值对应的单个对象。
通过相同的逻辑,对于HashMap内的对象,只有在hashCode()方法为两个对象返回相同的哈希值时才会调用equals()方法。
因此,在实现自定义类时,始终建议不仅要覆盖equals()方法,还要覆盖hashCode()方法。