java – 恢复哈希码“sum”

所以,不是每次都计算哈希码,我想我可以保持一个更新所有更改的整数.想象一下有助于哈希码的属性的setter:

public void setFoo(Foo newFoo){
    this.hashCalculator.remove(this.foo.hashCode()); // remove old hash code
    this.hashCalculator.add(newFoo.hashCode()); // add new hash code
    this.foo = newFoo; // set new foo
}

(我希望我没有做一些愚蠢的事情)我认为这将是简单的数学,但我没有实现它.我认为它与溢出的整数有关,但我不确定.显然我错过了一些东西.该部门的剩余部分应该可以加回到价值中,对吧?

这是我的代码.结果应该是1,但这不是我得到的.

class HashCode
{
    public int value = 1;

    public void add(int val){
        // as suggested by effective java
        value = value * 37 + val;
    }

    public void remove(int val){
        value = (value - val) / 37;
    }
}

HashCode o = new HashCode();

for(int a = 0; a < 1000; a++){
    o.add(a);
}

for(int r = 0; r < 1000; r++){
    o.remove(r);
}

System.out.println(o.value); // should be 1

解决方法

第一:无法正确反转导致整数溢出的操作.基本上问题是您无法知道整数溢出是否在前一个操作中发生过一次,两次甚至更多次.因此,在最后一次哈希计算之前,您无法获得原始值.

第二:哈希计算取决于应用的哈希值的顺序.

((1 * 37 + 0) * 37 + 1) * 37 + 2 = 50692
((1 * 37 + 2) * 37 + 1) * 37 + 0 = 53428
           ^         ^         ^ the hash values.

由于附加最后一个值的哈希值更改取决于所有先前的哈希值,您不能只更改一个中间哈希值,因此没有(表现良好)方法来消除前一个示例中的1对所有未来计算的影响.

如果您使用1,2,3等测试循环而不是1000开始,这应该是显而易见的.如果没有整数溢出,只有在按照添加它们的相反顺序删除哈希值时,循环才会起作用.这是一种在“现实生活”中应用没有意义的限制. @JB Nizet在his comment写的内容在我看来是正确的.

相关文章

Java中的String是不可变对象 在面向对象及函数编程语言中,不...
String, StringBuffer 和 StringBuilder 可变性 String不可变...
序列化:把对象转换为字节序列的过程称为对象的序列化. 反序...
先说结论,是对象!可以继续往下看 数组是不是对象 什么是对...
为什么浮点数 float 或 double 运算的时候会有精度丢失的风险...
面试题引入 这里引申出一个经典问题,看下面代码 Integer a ...