只有两个int属性的定制类的hashCode是什么?

2022-05-16 00:00:00 equals java hashcode

在Java中,我有一个用int坐标表示点的类

public class Point {
    int x = -1;
    int y = -1;

    public Point (int xNew, int yNew) {
        x = xNew; y = yNew;
    }

    public boolean equals (Object o) {
        // no need for (o instanceof Point) by design
        return x == ((Point)o).x && y == ((Point)o).y;
    }
}

我使用Point类的对象作为HashMap中的键,并作为HashSet中的元素。

hashCode函数的最佳候选者是什么?我会将其加倍,这样左边的部分是x,右边的部分是y,例如: x = 4, y = 12,则hashCode返回4.12。但通过实现,它不能是Double,只能是int。

这不是一个选项:

public int hashCode() {
    // no need to check for exception parseInt since x and y are valid by design
    return Integer.parseInt(Integer.toString(x) + Integer.toString(y));
}

因为值xy可能太长,所以它们加在一起不会被转换。


解决方案

您不能更改hashCode的类型,也不应该更改。

我只会这样做:

public int hashCode() {
    return x * 31 + y;
}
请注意,这意味着在大多数情况下(a,b)不同于(b,a)(不同于加法或异或运算)。如果您在实际生活中经常使用"已切换"值的键,这可能会很有用。

它不是唯一的-但散列代码不一定是唯一的。对于相同的值,只是必须相同(为了正确),而对于不相等的值,它们"通常"是不同的,并具有合理的分布。

总的来说,我通常遵循Josh Bloch在《Efficient Java:

》中建议的模式:

public int hashCode() {
    int hash = 17;
    hash = hash * 31 + field1Hash;
    hash = hash * 31 + field2Hash;
    hash = hash * 31 + field3Hash;
    hash = hash * 31 + field4Hash;
    ...
    return hash;
}

其中field1Hash将是引用类型字段的散列代码(或0表示空引用),int本身是整数值的散列代码,long是从64位到32位的某种散列,等等。

编辑:我记不清为什么31和17在一起工作得很好的细节。它们都是素数这一事实可能有用--但据我所知,这种散列背后的数学原理通常是合理的(尽管不如预先知道可能值分布的散列好),要么很难理解,要么就不能很好地理解。我知道乘以31比较便宜(左移5并减去原值)...

相关文章