普通程序员是否有“足够好”的哈希函数?

时间:2009-11-06 19:42:07

标签: language-agnostic hash

我们被告知我们应该为我们的类实现hashCode(),但是像我这样的大多数人并不知道如何做到这一点或者如果我们把它“弄错”会发生什么。例如,我需要一个哈希函数来索引树中的节点(Finding the most frequent subtrees in a collection of (parse) trees)。在这种情况下,我需要基于有序的子节点递归生成哈希码,例如

hashCode = function(child1.hashCode, child2.hashCode, ...)

recent discussion的hashCodes中,答案包括字符串的散列(基于长素数和31)以及位移。 String哈希是:

// adapted from String.hashCode()
public static long hash(String string) {
  long h = 1125899906842597L; // prime
  int len = string.length();

  for (int i = 0; i < len; i++) {
    h = 31*h + string.charAt(i);
  }
  return h;
}

我对安全性不感兴趣,也不介意碰撞。是否存在一个“通用函数”,用于组合有序对象的哈希码,它们会比损害更好(并且比完全不调用它更好)?

还有一个我们可以查找常见案例的网站吗?字符串,列表等。)

我没有指定语言,因为我希望有通用方法。但如果它是严格的语言,那么请说明语言以及为什么它不是普遍的。

更新两个建议是使用IDE的hashCode生成器。这似乎是一个很好的默认;这是Netbeans:

public int hashCode() {
    int hash = 5;
// objects
    hash = 97 * hash + (this.rootElement != null ? this.rootElement.hashCode() : 0);
    hash = 97 * hash + (this.tableElement != null ? this.tableElement.hashCode() : 0);
// a string
    hash = 97 * hash + (this.tag != null ? this.tag.hashCode() : 0);
    return hash;
}

7 个答案:

答案 0 :(得分:4)

Joshua Bloch的Effective Java中有一个很棒的hashCode()。示例第3章“所有对象共有的方法” 是免费的(好吧,当Sun的旧网站上有一个页面时它曾经回来了。如果你搜索,你仍然可以找到PDF版本那一章躺在某处。)

您还可以查看Apache Commons Lang中HashCodeBuilder的来源,但不要在没有引用的情况下将其复制到课程中。现在花时间去了解这一点 - 它会让你成为一个更好的人。

答案 1 :(得分:2)

虽然遗漏了标签,但我会假设你在谈论Java。

Eclipse 3.5附带了一个“懒惰”解决方案,只需按一下按钮即可为您生成哈希码。 toString()和equals()也是。非常好!我怀疑你可以在IDEA和NetBeans中找到类似的功能。

除此之外,实际上任何为同一输入始终生成相同值的散列函数都可以。这(可能)只会影响像HashMaps这样的东西的效率。

答案 2 :(得分:2)

如果您正在谈论定义自定义类的哈希码,最好的办法是定义所有字段哈希码函数的某种数学级联。

在定义哈希码时,您的目标通常是最大限度地减少冲突,因此如果您执行此类操作,您通常会处于明确状态。

hashcode=(field1.hashcode()*7)+(field2.hashcode()*3)+(field3.hashcode()*51)...

答案 3 :(得分:1)

如果您使用的是Windows,则可以使用HashData()

答案 4 :(得分:1)

这是我使用的哈希码组合函数(在C#中):

public static int CombineHashCodes(params int[] hashCodes)
{
    unchecked
    {
        var result = 0;
        foreach (var hash in hashCodes)
            result = (result * 397) ^ hash;
        return result;
    }
}

直观的推理是组合方面是XOR运算符。这就是.NET 4为元组做的事情:

public static int CombineHashCodes(int h1, int h2)
{
    return ((h1 << 5) + h1) ^ h2;
} 

答案 5 :(得分:0)

在任何托管环境中,对象哈希函数的原始实现是内存地址本身。如果您不关心散列函数的属性,只要在表示相同值的单独实例之间存在某种等价关系,任何值都可以。

如果您熟悉关系数据库设计,请考虑对象的主键?什么值构成主键?

说它是a, b and c,那么你的hashCode实现就像这样

return a.hashCode() ^ b.hashCode() ^ c.hashCode();

^是XOR(异或)逐位运算,这种方式可以将任意数量的值链接在一起形成哈希值,并且仍然保持适当的传播。

答案 6 :(得分:0)

回答你可能出错的问题:如果你把它放在哈希表(字典/地图)中,你的函数生成的哈希码将用于查找你的类实例的位置。如果你的哈希函数产生了太多的冲突,那么哈希表的性能可能与O(n)一样糟糕。