2

当我编译并运行下面的代码时,我得到以下结果:

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 强制执行,并且在违反合同时我会立即看到错误或异常......

4

7 回答 7

2

因为 JVM 不检查或验证方法契约是否成立。它们只是方法,它们可以返回任何他们想要的东西。

但是,任何依赖于它们支持方法契约的代码都可能会失败,也可能会失败。例如,您将无法EqualsAndHashCode在 a 中使用您的对象HashMap。在大多数情况下,这引发异常或不会返回正确的值。

这与compareTo()and TreeMaps-compareTo()可以返回任何int它想要的东西是一样的,但是如果它没有返回接口中方法合同定义的一致顺序,那么一旦检测到不一致Comparable,你就会抛出异常。TreeMap

于 2013-07-01T15:59:48.353 回答
2

那么,这段代码如何不会导致异常或错误呢?

好吧,打破 equals 和 hashcode 的约定,永远不会抛出异常或错误。只是当您在基于哈希的集合中使用这些类的对象时,您会看到奇怪的行为,例如 -HashSetHashMap.

例如,如果在您的情况下,您将类对象用作a 中的HashMap,那么当您尝试获取它时,您可能无法再次找到该键。因为,即使您的密钥相等,它们的哈希码也可能不同。和 HashMap saerch 首先使用它们的哈希码,然后使用equals键。

于 2013-07-01T16:00:57.460 回答
2

如果两个对象相等,它们的 hashCodes 也必须相等

以上是建议,并非 JVM 强制要求

此建议背后的想法是在哈希集合(例如 HashMap)中存储元素时减少冲突。

关于hashcode的需要,equals和hashcode的规则等非常好的文章:

http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html

于 2013-07-01T15:58:11.007 回答
0

hashcode() 和 equals() 的默认实现由您定义的每个类从 Object 类继承。为了让您的代码正常运行,尤其是在 HashMap 等数据结构中使用时,重要的是您“应该”覆盖默认实现,以确保“如果您的类的两个实例相等,则它们返回相同调用 hashCode() 方法时的值”。

两个对象相等的定义取决于它们的类所代表的域概念,因此,只有类的作者最适合实现“equals”和“hashcode”方法。例如,如果两个 Employee 对象具有相同的“employeeId”属性值,则它们被认为是相等的。这两个可能是不同的实例,但在域领域(例如,人力资源系统),由于员工 ID 相同,它们是相同的。现在,Employee 类的作者应该实现“equals”方法来比较“employeeId”属性,如果它们相同则返回true。同样,如果两个 Employee 实例的员工 ID 相同,则作者应确保其 hashCode() 相同。

如果您担心如何编写符合上述 Java 推荐的 hashCode,那么您可以使用 Eclipse 生成 hashCode 和 equals。

虽然这只是一个建议“如果两个对象相等,它们的 hashCodes 也必须相等”,但您应该知道,如果您的类的对象在 Set、Map 等中使用,您的代码可能会开始出现错误行为。不要创建符合此建议的“equals”和“hashCode”方法。只有当您确定您的班级永远不会被测试是否平等时,您才想忽略此建议。这种类的一个示例可以是 DAO 类或服务类,它们通常被实例化并用作单例,并且没有人(在正常情况下)比较两个 DAO 或服务类

于 2013-07-01T16:08:44.347 回答
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;
}
于 2013-07-01T16:01:29.267 回答
0

将此用于 hasCose

 public int hashCode() {
    int result = num1;
    result = 31 * result + num2;
    return result;
}
于 2013-07-01T16:02:55.733 回答
0

在大多数情况下,方法契约的目的是允许其他代码假设某些条件成立。特别是,hashCodeandequals契约的目的是允许集合假设如果一个对象Foo具有特定的哈希码(例如 24601),并且Bar已知对象集合不包含任何具有该哈希码的对象,则可以从中推断出Bar不包含的信息Foo。作为奖励,如果一个对象集合包含各种哈希码,Foo包括Bar在查看对象本身之前。无论对象多么复杂,比较两个对象的已计算哈希值都会很快。

为了使所有这些工作,一个将报告自己等于另一个对象的对象必须始终报告与另一个对象相同的哈希码。因为总是有可能遵守这条规则,所以很少有理由不遵守它。即使用于确定相等性的对象的唯一不可变特征是它的类型,仍然可以通过让该类型的所有对象返回相同的哈希值来遵守规则。让总是不同的对象报告不同的哈希值可以将性能提高几个数量级,但是在缓慢但正确的行为和快速但错误的行为之间进行选择,通常应该首选前者。

于 2013-07-01T16:25:18.647 回答