基于哈希码实现Equals

时间:2015-10-20 22:49:31

标签: java hash

根据equals和hashCode合约规则,下面的代码有什么问题吗?

class Test {
    @Override
    public int hashCode() {
        int hash = 0;
        //Calculate hash
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (this == obj)
            return true;
        if (getClass() != obj.getClass())
            return false;
        Test other = (Test) obj;
        if(this.hashCode() == other.hashCode()) {
            return true;
        } else {
            return false;
        }
    }
}

4 个答案:

答案 0 :(得分:2)

根据hashcode / equals合同,没有任何错误。

但这并不一定能使代码正确。这完全取决于类的必需的等式语义

例如,如果Test对象可能需要超过2 ^ 32个不同的状态,那么这种方法可能无法工作...因为只有2 ^ 32个不同的值可以由hashCode()方法返回。即使在sub 2 ^ 32的情况下,你依赖于类的完美散列函数的存在。找到/写一个函数永远不会实用。

另一点需要注意的是,您的方法通常不如以正常方式实现equals效率低。为什么?比较这个例子:

class Test {
    private boolean field1;
    private boolean field2;

    @Override
    public int hashCode() {
        return (field1 ? 0 : 1) * 2 + (field2 ? 0 : 1);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (this == obj)
            return true;
        if (getClass() != obj.getClass())
            return false;
        Test other = (Test) obj;
        // version 1
        if(this.hashCode() == other.hashCode()) {
            return true;
        } else {
            return false;
        }
        // version 2
        if (this.field1 == other.field1 && this.field2 == other.field2) {
            return true;
        } else {
            return false;
        }
    }
}

现在比较"版本1"和"版本2"片段:

  • 版本1 - 2个方法调用,5个比较和分支以及1个乘法。
  • 版本2 - 0方法调用,2个比较和分支以及0个乘法。

现在JIT编译器可能会显着优化,但在这种情况下,哈希码的计算可能会超过任何节省量。即使您缓存了哈希码(例如String),您也需要条件为"恰到好处"基于哈希码的方法更好。

答案 1 :(得分:1)

假设你有hashCode的具体实现,而不是"计算哈希"你的代码是正确的,甚至对某些情况也有意义。

例如,如果Test基本上是Boolean,即它只能有两个值。然后,如果您将hashCode定义为:

 public int hashCode() {
    return value ? 1 : 0;
 }

您的equals方法将完全正常运行。

另一个类似示例,如果您的Test类是Integer值对象。如果您将value作为hash返回,则等号将起作用并且在逻辑上是正确的。

基本规则是,只要您可以定义hashCode以便它涵盖您班级的所有可能值,那么您就是好的。否则,equals实现只是没有意义,因为它可以为某些逻辑上不同的对象返回true

P.S。对于那些说这个实现违反equals-hashcode合同的人。你完全错了。由于此实施的equalshashCode同等作用,因此无法按照定义违反合同。

答案 2 :(得分:0)

这是不正确的,因为the hashCode method不需要为对象的所有可能状态返回不同的int

  

如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。

理论上,两个对象可能彼此不相等,但它们返回相同的哈希码。

null,相同参考和不同类的测试似乎是正确的。

但是,您不必依赖哈希码来确定相等性,而是必须根据对象自己的字段和自己的相等标准进行自己的比较。

答案 3 :(得分:0)

两点。首先,不要从int hash = 0;开始更好地从正值开始,比如说:int hash = 17;这样可以避免将初始0乘以某些输入的危险,从而在最终输出中失去输入的影响哈希值。

其次,您对null的明确测试是不必要的。尝试使用:

if (!(obj instanceOf MyType)) {
  return false;
} else {
  MyType myty = (MyType)obj;
  ...
}

如果instanceOf为空或不属于false,则obj运算符将返回MyType