我试图通过添加一个新字符来测试String
哈希码如何工作,并反过来将其减去,但是当我进行计算时它并没有给我正确的答案。
例如
int PRIME = 31;
//this will print 1687846330
System.out.println("mlkjihgfedcb".hashCode());
//this will print 783628775 which equals to "mlkjihgfedcba".hashCode();
System.out.println(("mlkjihgfedcb".hashCode() * PRIME + (int) 'a'));
//this will print 25278344 which doesn't equals to "mlkjihgfedcb".hashCode()
System.out.println(("mlkjihgfedcba".hashCode() - (int) 'a') / PRIME);
我想知道我在上一步做了数学吗?溢出对计算权无关紧要?
答案 0 :(得分:4)
散列函数不是可逆函数。 作为证明,考虑Pigeonhole principle,您只能在hashCode中存储整数范围内的值,但是有无限数量的字符串。因此,必须有多个具有相同hashCode的字符串(并且有)。
答案 1 :(得分:4)
将大数乘以31会导致整数溢出,使用除以31后无法反转。
但请坚持:在Java中使用int
的算术等同于算术模2 32 。由于31是奇数,因此它具有multiplicative inverse模2 32 。因此乘以31可以通过乘以所述“逆”来反转。那就是:
int inverse = -1108378657;
// This will print the hashCode of mlkjihgfedcb
System.out.println(("mlkjihgfedcba".hashCode() - (int) 'a') * inverse);
答案 2 :(得分:1)
失败的具体原因是
1687846330 * 31 + 97 = 52323236327
数字52,323,236,327远远大于int
所能容纳的数字,因此信息会从左端丢失。通过反转hashCode()
算法无法恢复该信息。
如果你进行算术运算,你会看到你为第二个字符串得到的哈希码恰好是 52323236327 mod 2 31
更重要的是,从对象到哈希码的映射旨在不透明且不可逆。仅仅因为它今天以特定的方式实现并不意味着库的下一个版本必须以相同的方式执行它。唯一的保证是碰撞的可能性极低(但非零)。