繁体   English   中英

为什么等于&hashCode覆盖不会导致异常或错误?

[英]How come this equals & hashCode override does NOT cause an exception or error?

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

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

根据我的阅读,如果两个对象相等,则它们的hashCode也必须相等。 那么,此代码如何不会导致异常或错误?

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方法必须产生相同的整数结果。

that this rule would have been enforced by the JVM at compile or run time and I would have seen errors or exceptions right away when the contract was violated... 该规则将由JVM在编译或运行时强制执行,并且在违反合同时立即会看到错误或异常...

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

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

此建议的思想是在将元素存储在HashMap之类的哈希集合中时减少冲突。

关于哈希码的需求,等于和哈希码的规则等非常好的文章:

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

因为JVM不检查或验证方法协定是否成立。 它们只是方法,它们可以返回所需的任何内容。

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

这与compareTo()TreeMaps相同TreeMaps compareTo()可以返回它想要的任何int ,但是如果它没有返回Comparable接口中的方法协定所定义的一致顺序,则TreeMap将引发异常一旦发现不一致之处。

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

好吧,打破相等和哈希码的约定,永远不会引发异常或错误。 在基于哈希的集合(例如HashSetHashMap使用这些类的对象时,只会看到奇怪的行为。

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

考虑到您要除以随机数的事实,它们怎么可能一样?

典型的方法是使用各个字段的hashCode值来构建对象的hashCode(如果不是原始类型,则为哈希类型)。 您通常还会乘以几个质数。

// adapted from Effective Java
public int hashCode() {
   int p = 17, q = 37;

   p = q * p + num1;
   p = q * p + num2;

   return p;
}

将此用于hasCose

 public int hashCode() {
    int result = num1;
    result = 31 * result + num2;
    return result;
}

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

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

而且,如果您担心如何编写满足上述Java建议的hashCode,则可以使用Eclipse生成hashCode和equals。

尽管仅建议“如果两个对象相等,则它们的hashCode也必须相等”,但是您应该意识到以下事实:如果在Set,Map等中使用了类的对象,则代码可能会出现异常。不要创建符合此建议的“等于”和“ hashCode”方法。 只有在您确定绝对不会对类进行测试时,才想忽略此建议。 此类的示例可以是DAO类或Service类,它们通常被实例化并用作Singleton,并且没有人(在正常情况下)比较两个DAO或Service类

在大多数情况下,方法合同的目的是允许其他代码假定某些条件成立。 特别地, hashCodeequals合约的目的是允许集合假设如果对象Foo具有特定的哈希码(例如24601),并且已知对象Bar的集合不包含任何具有该哈希码的对象,则可以从该信息推断出Bar不包含Foo 另外,如果一组对象包含各种哈希码,包括Foo的哈希码,并且如果已经预先计算了该集合中所有对象的哈希码,则可以在查看该对象之前将其与Bar的哈希码进行比较。对象本身。 不管对象多么复杂,比较两个对象的已计算哈希值都将很快。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM