我对负双值哈希码和正双值哈希码有一些奇怪的行为,这是我没有预料到的。本质上,如果相同的 Double 值在 hashcode 函数中出现两次,如果该 Double 的符号发生变化 (pos<->neg),则 hashcode 保持不变。例如:
public final int hashCode() {
int result = ...
result = 31 * result + netAmount.hashCode();
result = 31 * result + grossAmount.hashCode();
...
return result;
}
使用 jshell 并查看二进制文件中的哈希码,如果乘以 2,就会得到相同的值:
Double.valueOf("460.4").hashCode() = 639279104
Double.valueOf("-460.4").hashCode() = -1508204544
Double.valueOf("460.4").hashCode() * 2 = 1278558208
Double.valueOf("-460.4").hashCode() * 2 = 1278558208
好吧,这是有道理的。但在我的哈希码函数中,我在每一步后乘以 31,这样应该可以防止这种情况发生吗?也许不是。将我的哈希码函数转换为单行函数:
31 * (123 + Double.valueOf("460.4").hashCode()) + Double.valueOf("460.4").hashCode() = -1017901339
31 * (123 + Double.valueOf("-460.4").hashCode()) + Double.valueOf("-460.4").hashCode() = -1017901339
如果我对此进行数学,那么我们会看到它变成:
31*123 + 32*Double.valueOf("460.4").hashCode() = -1017901339
31*123 + 32*Double.valueOf("-460.4").hashCode() = -1017901339
所以这里的 32 具有相同的位移效果,这意味着正 double 值最终与负数相同。狂野!
为了防止这些冲突,我应该在这里做什么?只需使用 37 或其他数字?将双精度数的符号的哈希码添加到结果中?
所以你在这里处理整数溢出。
hashCode
返回 int
:
public int hashCode()
返回对象的哈希码值。支持此方法是为了哈希表(例如 HashMap 提供的哈希表)的好处。
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html#hashCode()
返回类型,
int
是32位:即使只是将
hashCode
乘以 2 也会导致溢出(丢弃最左边的位,又称符号位),使两个结果值相等:Double.valueOf(" 460.4").hashCode() -> 00100110 00011010 10100000 00000000
Double.valueOf("-460.4").hashCode() -> 10100110 00011010 10100000 00000000
Double.valueOf(" 460.4").hashCode() * 2 -> |0| 01001100 00110101 01000000 00000000
Double.valueOf("-460.4").hashCode() * 2 -> |1| 01001100 00110101 01000000 00000000
相应的
|0|
和 |1|
表示乘以 2 后删除了哪一位(这本质上是一次左移,因为它是 2 的幂)。
乘以 32(再次左移 5,因为 32 是 2 的幂),会导致更多位被丢弃:
...(" 460.4").hashCode() * 32 -> |00100| 11000011 01010100 00000000 00000000
...("-460.4").hashCode() * 32 -> |10100| 11000011 01010100 00000000 00000000
再次删除
||
中的位。
我不确定您的示例中的
123
或 31
来自何处,但这里有一些建议:
Double
或Long
)进行数学计算hashCode
hashCode
合同(复制如下)。第三点与这里非常相关。在 Java 应用程序执行期间,只要在同一对象上多次调用 hashCode 方法,只要不修改对象的 equals 比较中使用的信息,hashCode 方法就必须始终返回相同的整数。从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。
如果根据 equals 方法两个对象相等,则对这两个对象调用 hashCode 方法必须产生相同的整数结果。
不要求如果两个对象根据 equals 方法不相等,则对这两个对象调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。