当我编译并运行下面的代码时,我得到以下结果:
o1==o2 ? true
Hash codes: 0 | 0
o1==o2 ? true
Hash codes: 1 | 8
o1==o2 ? true
Hash codes: 7 | 3
o1==o2 ? true
Hash codes: 68 | 10
o1==o2 ? true
Hash codes: 5 | 4
从我读过的内容来看,如果两个对象相等,则它们的hashCodes也必须相等。那么,这段代码怎么不会导致异常或错误?
import java.io.*;
import java.lang.*;
public class EqualsAndHashCode {
private int num1;
private int num2;
public EqualsAndHashCode(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}
public static void main(String[] args) {
for (int x=0; x < 5; x++) {
EqualsAndHashCode o1 = new EqualsAndHashCode(x, x);
EqualsAndHashCode o2 = new EqualsAndHashCode(x, x);
System.out.println("o1==o2 ? " + o1.equals(o2));
System.out.println("Hash codes: " + o1.hashCode() + " | " + o2.hashCode());
}
}
public boolean equals(Object o) {
return (this.getNum1() == ((EqualsAndHashCode)o).getNum1()) && (this.getNum2() == ((EqualsAndHashCode)o).getNum2());
}
public int hashCode() {
return (int)(this.getNum1() / Math.random());
}
public int getNum1() { return num1; }
public int getNum2() { return num2; }
}
编辑我 我的问题背后的前提是围绕hashCode合约(http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode())的措辞:
如果两个对象根据equals(Object)方法相等,那么 必须在两个对象中的每一个上调用hashCode方法 相同的整数结果。
我假设该规则将在编译或运行时由JVM强制执行,并且当合同被违反时我会立即看到错误或异常......
答案 0 :(得分:2)
如果两个对象相等,则它们的hashCodes也必须相等
以上是建议,JVM不强制
这个建议背后的想法是在散列集合(如HashMap)中存储元素时减少冲突。
关于hashcode需求,equals和hashcode规则等的非常好的文章:
http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html
答案 1 :(得分:2)
因为JVM不检查或验证方法契约是否成立。他们只是方法,他们可以回报任何他们想要的东西。
但是,任何依赖于它们支持方法合同的代码都可能会或将会失败。例如,您将无法在EqualsAndHashCode
中使用HashMap
个对象。在大多数情况下,将抛出异常或不会返回正确的值。
这与compareTo()
和TreeMaps
相同 - compareTo()
可以返回它想要的任何int
,但如果它没有返回定义的一致排序通过Comparable
界面中的方法合同,一旦检测到不一致,您的TreeMap
就会抛出异常。
答案 2 :(得分:2)
那么,这段代码怎么不会导致异常或错误?
好吧,打破equals和hashcode的契约,永远不会抛出异常或错误。当您在基于散列的集合中使用这些类的对象时,您会看到奇怪的行为,例如 - HashSet
或HashMap
。
例如,如果在您的情况下,您在HashMap
中将类对象用作键,则在尝试获取时可能无法再次找到该键它。因为,即使您的密钥相等,它们的哈希码也可能不同。 HashMap首先使用哈希码,然后使用等于来搜索密钥。
答案 3 :(得分:0)
考虑到你用随机数除以它们,它们怎么可能是相同的?
典型的方法是使用各个字段的hashCode值来构建对象的hashCode(如果它们不是原始的,在这种情况下它们是原始的)。您通常还会乘以几个素数。
// adapted from Effective Java
public int hashCode() {
int p = 17, q = 37;
p = q * p + num1;
p = q * p + num2;
return p;
}
答案 4 :(得分:0)
将此用于hasCose
public int hashCode() {
int result = num1;
result = 31 * result + num2;
return result;
}
答案 5 :(得分:0)
hashcode()和equals()的默认实现由您定义的每个类从Object类中隐藏。为了使代码行为正常,特别是在HashMap等数据结构中使用时,重要的是“应”覆盖默认实现,以确保“如果类的两个实例相等,则返回相同调用hashCode()方法时的值“。
两个对象的相等性的定义取决于它们的类所代表的域概念,因此,只有该类的作者最适合实现“equals”和“hashcode”方法。例如,如果两个Employee对象具有“employeeId”属性的相同值,则它们被视为相等。这两个可能是不同的实例,但在域(例如,人力资源系统)领域,由于其员工ID相等,它们是相同的。现在,Employee类的作者应该实现比较“employeeId”属性的“equals”方法,如果它们相同则返回true。同样,如果员工ID相同,则作者应确保两个Employee实例的hashCode()相同。
如果您担心如何编写符合上述Java建议的hashCode,那么,您可以使用Eclipse生成hashCode和equals。
虽然只是建议“如果两个对象相等,它们的hashCodes也必须相等”,你应该知道,如果在Set,Map等中使用了类的对象,你的代码就会出现行为不端的事实。 。如果您不创建符合此建议的“equals”和“hashCode”方法。只有当您确定您的类永远不会被测试为相等时,您才想忽略此建议。此类的一个示例可以是DAO类或Service类,它通常被实例化并用作Singleton,并且没有人(在正常情况下)比较两个DAO或Service类
答案 6 :(得分:0)
方法契约的目的在于大多数情况下允许其他代码假设某些条件成立。特别是,hashCode
和equals
契约的目的是允许集合假设对象Foo
具有特定哈希码(例如24601)和对象集合{已知{1}}不包含具有该哈希码的任何对象,可以从该Bar
不包含Bar
的信息推断出。作为奖励,如果对象集合包含各种哈希码,包括Foo
的哈希码,并且如果预先计算了集合中所有对象的哈希码,则可以检查每个对象的哈希码与{ {1}}在查看对象本身之前。无论对象有多复杂,比较两个对象已经计算好的哈希值都会很快。
要使所有这些工作,必须将报告自身等于另一个对象的对象总是报告与其他对象相同的哈希码。因为总是可以遵守这个规则,所以很少有任何理由不服从它。即使用于确定相等性的对象的唯一不可变特性是其类型,仍然可以通过使该类型的所有对象返回相同的散列值来遵守该规则。具有总是不同的对象报告不同的散列值可以将性能提高几个数量级,但是在行为缓慢但正确的行为和快速但错误的行为之间进行选择,前者通常应该是优选的。