我为我的班级定义了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());
}
答案 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}}马上!