理解哈希码

时间:2010-06-25 21:18:54

标签: java hash

哈希函数在实现哈希表时很重要。我知道在java中 Object有哈希码,可能是从弱哈希函数生成的。

以下是一个“补充哈希函数”的片段

static int hash(Object x) {
    int h = x.hashCode();

    h += ~(h << 9);
    h ^=  (h >>> 14);
    h +=  (h << 4);
    h ^=  (h >>> 10);
    return h;
}

任何人都可以帮助解释哈希算法的基本思想 ?生成非重复整数?如果是这样,这些如何按位 操作成功吗?

6 个答案:

答案 0 :(得分:5)

散列函数是任何明确定义的过程或数学函数,它将大量可能可变大小的数据转换为小数据,通常是可以作为数组索引的单个整数。哈希函数返回的值称为哈希值,哈希码,哈希值,校验和或简单哈希值。 (wikipedia

使用更多“人类”语言对象哈希是基于对象属性的简短紧凑值。也就是说,如果您有两个以某种方式变化的对象 - 您可以预期它们的哈希值会有所不同。好的哈希算法为不同的对象产生不同的值。

答案 1 :(得分:1)

您通常尝试使用哈希算法将大搜索键转换为小的非负数,这样您就可以在某个表中查找相关记录,并且比M log2 N更快(其中M是“比较”的成本,N是“表格”中的项目数,是典型的二分搜索(或树搜索)。

如果你足够幸运拥有一个完美的哈希,你知道你的(已知!)键集的任何元素都将被散列为一个唯一的,不同的值。完美的哈希主要是对需要查找语言关键词的编译器感兴趣。

在现实世界中,你有不完美的哈希值,其中几个键都散列到相同的值。没关系:你现在只需将密钥与一小组候选匹配(散列到该值的匹配)进行比较,而不是大集(全表)。小集合传统上称为“桶”。您使用哈希算法选择存储桶,然后为存储桶本身使用其他一些可搜索的数据结构。 (如果桶中元素的数量已知或安全预期,实际上很小,线性搜索并非不合理。二进制搜索树也是合理的。)

您的示例中的按位操作看起来很像签名分析移位寄存器,它尝试将长的唯一位模式压缩为一种简短但仍然唯一的模式。

答案 2 :(得分:1)

基本上,您尝试使用散列函数实现的是在给定要散列的特定项目时,给散列码中的所有位大约50%的机会关闭或打开。这样,你的哈希表有多少“桶”(或换句话说,为了确定桶号你需要多少底部位)并不重要 - 如果每个 bit尽可能随机,然后一个项目将始终分配给一个基本上随机的桶。

现在,在现实生活中,许多人使用不那么好的哈希函数。它们在某些位中具有一些随机性,但不是全部。例如,想象一下如果你有一个哈希函数,其位6-7有偏差 - 让我们说在一个对象的典型哈希码中,它们有75%的机会被设置。在这个组成的例子中,如果我们的哈希表有256个桶(即桶号来自哈希码的0-7位),那么我们就丢弃了8-31位中存在的随机性,并且更小桶的一部分将倾向于被填充(即那些数字具有第6和第7位设置的那些)。

补充散列函数基本上试图在更大数量的位上扩展散列码中的任何随机性。因此,在我们的假设示例中,想法是来自位8-31的一些随机性将与低位混合,并稀释位6-7的偏差。它仍然不会是完美的,但比以前更好。

答案 3 :(得分:1)

如果您正在生成哈希表,那么在编写哈希函数时,您想要了解的主要内容是确保一致性,而不一定要创建完全唯一的值。

例如,如果您有一个大小为10的哈希表,则不希望哈希函数一次又一次地返回哈希值3。否则,该特定桶将强制搜索时间为O(n)。你想要一个哈希函数,它会返回,例如:1,9,4,6,8 ...并确保你的桶没有比其他桶重得多。

对于您的项目,我建议您使用众所周知的哈希算法,如MD5甚至更好的SHA,并使用您需要的前k位并丢弃其余的。这些是经过时间考验的功能,作为程序员,您可以聪明地使用它们。

答案 4 :(得分:0)

该代码试图通过捣碎周围的位来提高哈希值的质量。

总体效果是,对于给定的x.hashCode(),您希望在整个整数范围内更好地分配哈希值。如果您从糟糕的哈希码实现开始,然后以这种方式改进哈希码,某些算法的性能将得到改善。

例如,Java中一个简陋的Integer的hashCode()只返回整数值。虽然这可以用于许多目的,但在某些情况下,您需要更好的哈希代码,因此通过这种函数放置hashCode会显着改善它。

答案 5 :(得分:0)

只要您遵守文档中描述的general contract,就可以是您想要的任何内容,用我自己的话来说就是:

  • 如果在对象上调用100(N)次hashCode,则所有时间必须返回相同的值,至少在该程序执行期间(后续程序执行可能返回不同的值)
  • 如果o1.equals(o2)为真,那么o1.hashCode() == o2.hashCode()也必须为
  • 如果o1.equals(o2)为假,那么o1.hashCode() == o2.hashCode()可能属实,但有帮助则不然。

就是这样。

根据类的性质,hashCode()e可能非常复杂或非常简单。例如,可能有数百万个实例的String类需要非常好的hashCode实现,并使用素数来减少碰撞的可能性。

如果你的班级有一个连续的数字确实有意义,那也没关系,你没有理由每次都要复杂化它。