从equals()调用hashCode()

时间:2015-04-27 18:26:33

标签: java equals hashcode

我为我的班级定义了hashCode(),并列出了很长的班级属性列表。

根据合同,我还需要实现equals(),但是可以通过比较hashCode()内部来实现它,以避免所有额外的代码吗?这样做有危险吗?

e.g。

@Override
public int hashCode() 
{
    return new HashCodeBuilder(17, 37)
        .append(field1)
        .append(field2)
    // etc.
    // ...
}

@Override
public boolean equals(Object that) {
    // Quick special cases
    if (that == null) {
        return false;
    }
    if (this == that) {
        return true;
    }
    // Now consider all main cases via hashCode()
    return (this.hashCode() == that.hashCode());
}

2 个答案:

答案 0 :(得分:14)

不要这样做。

hashCode()的合同说两个相等的对象必须具有相同的哈希码。对于不相等的对象,它不能保证任何东西。这意味着您可以拥有两个完全不同的对象,但偶然会碰巧具有相同的哈希码,从而破坏了equals()

在字符串之间获取哈希码冲突并不困难。考虑JDK 8 String.hashCode()实现的核心循环:

for (int i = 0; i < value.length; i++) {
    h = 31 * h + val[i];
}

h的初始值为0,而val[i]是给定字符串中 ith 位置中字符的数值。例如,如果我们采用长度为3的字符串,则此循环可以写为:

h = 31 * (31 * val[0] + val[1]) + val[2];

如果我们选择任意字符串,例如"abZ",我们就有:

h("abZ") = 31 * (31 * 'a' + 'b') + 'Z'
h("abZ") = 31 * (31 * 97 + 98) + 90
h("abZ") = 96345

然后我们可以从1中减去val[1],同时将31添加到val[2],这会为我们提供字符串"aay"

h("aay") = 31 * (31 * 'a' + 'a') + 'y'
h("aay") = 31 * (31 * 97 + 97) + 121
h("aay") = 96345

导致碰撞:h("abZ") == h("aay") == 96345

另请注意,您的equals()实现不会检查您是否在比较相同类型的对象。因此,假设您有this.hashCode() == 96345,以下语句将返回true

yourObject.equals(Integer.valueOf(96345))

这可能不是你想要的。

答案 1 :(得分:3)

仅仅比较对象的hashCode()肯定是安全。

您的对象可以具有与哈希码不同的状态:哈希代码是int,这意味着它被限制为2 ^ 32 = 4,294,967,296个可能的值,但您的对象可能会有多个int false 1}} field。

所以事实证明,可能有两个不同的对象(根据equals)具有相同的哈希码。

但是,当然,出于性能原因,您可以首先比较哈希码(如果哈希码计算比字段比较快):如果哈希码不相等,则对象也不相等,因此您可以安全地返回{{ 1}}马上!