有效地为Set <integer>的BitSet类实现计算hashCode

时间:2018-02-21 02:59:08

标签: java hashcode bitset

我想知道,如何有效地计算hashCode的{​​{1}} - 就像BitSet的实施一样。

Set<Integer>显然很快计算,相当愚蠢(*)并且与BitSet#hashCode不兼容。

快速兼容的实现可能就像

Set#hashCode()

如果有效的实施

int hashCode() {
    int result = 0;
    for (int i=0; i<bits.length; ++i) {
        long word = bits[i];
        result += 64 * i * Long.bitCount(word) + weightedBitCount(word);
    }
    return result;
}

如果大多数位未设置,可以通过测试int weightedBitCount(long word) { // naive implementation int result = 0; for (int i=0; i<64; ++i) { if ((word & (1L << i)) != 0) { result += i; } } return result; } 或使用word==0或类似的方式来改善天真的实现,但这些技巧不会有太大帮助,并且在其他情况下是有害的。

一般来说,有一个聪明的技巧可以显着提高它吗?

我想,对多个单词进行一些批量计算可能会更有效率。同时计算Long.highestOneBit将是一个很好的奖励。

关于过早优化的说明:是的,我知道。我非常好奇(对于像Project Euler这样的东西很有用)。

(*)有许多位被完全忽略(它们在乘法中被移出)。

2 个答案:

答案 0 :(得分:2)

我认为将散列冲突与散列性能相结合也很重要。更快的散列计算可以使您的程序通常更慢,因为大量的哈希未命中。

最好使用Google Guava中的MurMur3A之类的通用哈希函数,而不是发明自己的哈希函数。

有许多关于散列的基准测试,例如:

我认为您可以使用Google Caliper进行一些微基准测试,并检查哪种哈希函数更适合您。

顺便说一句。问你自己为什么需要自定义BitSet?

答案 1 :(得分:0)

这就是我所做的:

int weightedBitCount(long word) {
       return (Long.bitCount(word & 0xFFFF_FFFF_0000_0000L) << 5)
            + (Long.bitCount(word & 0xFFFF_0000_FFFF_0000L) << 4)
            + (Long.bitCount(word & 0xFF00_FF00_FF00_FF00L) << 3)
            + (Long.bitCount(word & 0xF0F0_F0F0_F0F0_F0F0L) << 2)
            + (Long.bitCount(word & 0xCCCC_CCCC_CCCC_CCCCL) << 1)
            + (Long.bitCount(word & 0xAAAA_AAAA_AAAA_AAAAL) << 0);
}

非常简单:使用单个位设置,例如位10,word看起来像0x0000_0000_0000_0400L,只有掩码0xFF00_FF00_FF00_FF00L0xCCCC_CCCC_CCCC_CCCCL产生1位数,所以我们得到

(0 << 5) + (0 << 4) + (1 << 3) + (0 << 2) + (1 << 1) + (0 << 5) = 10

每64位需要一些6 * 4指令(现代英特尔可能有6个周期),所以它并不是很慢,但与需要的批量位集操作相比,它仍然太慢一条指令(每64位)。

所以我正在玩多个单词的批量计算。