有时我需要通过组合其几个实例成员的hashCodes来实现obj的hashCode()方法。例如,如果组合obj有成员a,b和c,我经常看到ppl将其实现为
int hashCode(){
return 31 * 31 * a.hashCode() + 31 * b.hashCode() + c.hashCode();
}
这个神奇的数字31来自哪里?它是4字节的长度还是只是素数?
是否有其他优选/标准的方法来实现hashCode()?
答案 0 :(得分:8)
见Effective Java's recipe。这只是最好的来源,放下手。
使用素数只是为了在不知道域的情况下尝试获得相当好的分布。溢出到相同的值需要一段时间。如果我没记错的话,值31非常随意。
根据布洛赫(他使用17作为初始值,37作为常数乘数):
使用非零初始值(...),因此哈希值会受到影响 哈希值(...)为零的初始字段。如果使用零则为 初始值(...)总体哈希值不受任何此类哈希值的影响 初始字段,可能会增加冲突。值17是任意的 ...
选择乘法器37是因为它是奇数素数。如果它是和 乘法溢出,信息会因为乘法而丢失 两个相当于移位。使用素数的优点较少 很清楚,但为此目的使用素数是传统的。
答案 1 :(得分:6)
一个不错的选择是Guava的Objects.hashCode
方法。它需要任意数量的参数并根据它们创建一个哈希码:
@Override public int hashCode() {
return Objects.hashCode(a, b, c);
}
答案 2 :(得分:2)
使用Commons Lang的HashCodeBuilder:
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
有关不使用反射的方法,请参阅API。您可以告诉它要包含哪些字段,或忽略哪些字段。
另请参阅EqualsBuilder,用于覆盖equals方法。
答案 3 :(得分:1)
使用IDE生成它。
答案 4 :(得分:0)
基本上,您的哈希码应该包含POJO的关键参数。一个例子如下。
public int hashCode() {
int hash = 0;
if (getRollId() != null) {
hash += getRollId().hashCode();
}
if (getName() != null) {
hash += getName().hashCode();
}
return hash == 0 ? System.identityHashCode(this) : hash;
}
在上面的示例中,roll id和name是该POJO的关键参数。
如果您只将这些参数添加到您在相同POJO的Eqauls方法中添加的hashCode方法,这是一个很好的做法。
答案 5 :(得分:0)
我认为以下是简单场景的好习惯: 如果您的类包含任何只读成员,它们将是生成对象的哈希码的良好候选者。 但是,如果您的类只包含变异成员,则可以创建一个readonly int字段,该字段根据传递给构造函数的非null值获取值。