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

Tos*_*kan 35 java performance caching hashmap immutability

所以我读到了HashMap.有人指出:

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

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

Jef*_*rey 33

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;
}
Run Code Online (Sandbox Code Playgroud)

由于一个String永不改变的内容,该类的制造者选择在计算一次之后缓存哈希.这样,时间不会浪费重新计算相同的值.

  • 只是为了记录:如果字符串是可变的,并且您在`HashMap`中使用它,那么在您修改字符串之后,哈希表将被破坏并且或多或少不可用.哈希表不知道基于新的哈希码将条目移动到其新位置. (12认同)

Nik*_* B. 10

引用链接的博客条目:

具有适当的equals()和hashcode()实现的final对象将充当完美的Java HashMap密钥,并通过减少冲突来提高Java hashMap的性能.

我看不出两者finalequals()有任何与哈希冲突.这句话引起了我对该条款可信度的怀疑.它似乎是教条Java"智慧"的集合.

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

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

  • HashMap缓存不可变对象的哈希码.这是不正确的.地图无法确定对象是否"不可变".
  • 对象需要不可变性来缓存其自己的哈希码.理想情况下,对象的哈希值应始终只依赖于对象的非变异状态,否则该对象无法合理地用作键.所以在这种情况下,作者也没有说明一点:如果我们假设我们的对象没有改变它的状态,我们也不必每次都重新计算哈希值,即使我们的对象是可变的!

所以,如果我们真的是疯了,居然决定使用List作为一个关键HashMap 使散列值依赖于内容,而不是列表的标识,我们可以只决定无效于每修改缓存的哈希值,从而将散列计算的数量限制为对列表的修改数量.


Rol*_*lig 6

这很简单.由于不可变对象不会随时间变化,因此只需要执行一次哈希码的计算.再次计算它将产生相同的值.因此,通常在构造函数(或懒惰)中计算哈希代码并将其存储在字段中.然后hashcode函数只返回字段的值,这确实非常快.