为什么哈希图中的不可变对象如此有效?

所以我读了关于HashMap的文章。有人指出:

“不可变性还允许缓存不同键的哈希码,这使得整个检索过程非常快,并建议Java Collection API提供的String和各种包装类(例如)是非常好的键。IntegerHashMap

我不太明白...为什么?


答案 1

String#hashCode:

private int hash;

...

public int hashCode() {
    int h = hash;
    if (h == 0 && count > 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

由于内容永远不会改变,因此该类的创建者选择在计算一次哈希后缓存哈希。这样,就不会浪费时间重新计算相同的值。String


答案 2

引用链接的博客文章:

具有适当等于 () 和哈希码 () 实现的最终对象将充当完美的 Java HashMap 键,并通过减少冲突来提高 Java hashMap 的性能。

我看不出两者如何,并且与哈希冲突有任何关系。这句话引起了我对文章可信度的怀疑。它似乎是教条式的Java“智慧”的集合。finalequals()

不可变性还允许缓存不同键的哈希码,这使得整个检索过程非常快,并建议String和各种包装类(例如Java Collection API提供的整数)是非常好的HashMap键。

我看到这句话有两种可能的解释,这两种解释都是错误的:

  • HashMap 缓存不可变对象的哈希代码。这是不正确的。地图无法确定对象是否“不可变”。
  • 不可变性是对象缓存自己的哈希代码所必需的。理想情况下,对象的哈希值应始终仅依赖于对象的非变异状态,否则无法合理地将对象用作键。因此,在这种情况下,作者也没有提出一个观点:如果我们假设我们的对象没有改变其状态,我们也不必每次都重新计算哈希值,即使我们的对象是可变的!

因此,如果我们真的疯了,并且实际上决定使用a作为a的键,并使哈希值依赖于内容,而不是列表的标识,我们可以决定在每次修改时使缓存的哈希值无效,从而将哈希计算的次数限制为对列表的修改次数。ListHashMap


推荐