【问题标题】:Is there a "good enough" hash function for the average programmer?对于普通程序员来说,是否有一个“足够好”的散列函数?
【发布时间】:2010-12-13 23:26:51
【问题描述】:

我们被告知应该为我们的类实现 hashCode(),但是像我这样的大多数人并不真正知道如何执行此操作,也不知道如果我们“错误”了会发生什么。例如,我需要一个哈希函数来索引树中的节点 (Finding the most frequent subtrees in a collection of (parse) trees)。在这种情况下,我需要根据有序的子节点递归生成哈希码,例如

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

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

// 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;
}

【问题讨论】:

    标签: language-agnostic hash


    【解决方案1】:

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

    【讨论】:

    • 如何在 C# 类中使用它?
    • 我很高兴了解 C# - 我没有特别指定 Java,我认为存在与语言无关的方法。
    • 通过 pinvoke / interop。与调用任何本机函数的方式相同。见pinvoke.net
    【解决方案2】:

    虽然缺少标签,但我假设您说的是 Java。

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

    除此之外,几乎任何哈希函数都可以为相同的输入始终生成相同的值。这将(可能)只会影响像 HashMaps 这样的东西的效率。

    【讨论】:

    • 一些常见的情况可以看Sun的JDK代码。如果我没记错的话,对于字符串,它只是不断乘以 37 并添加下一个字符的 int 值。对于列表,我认为它做了类似的事情,将结果乘以某个素数并添加(或异或)下一个对象的哈希码。
    【解决方案3】:

    Joshua Bloch 的Effective Java 中有一个出色的 hashCode()。示例第 3 章,“所有对象的通用方法”是免费的(嗯,它曾经在 Sun 的旧网站上有一个页面时返回。如果您搜索,您可能仍然会找到 PDF 版本那个章节在某个地方)。

    您也可以在 Apache Commons Lang 中查看 HashCodeBuilder 的源代码,但不要在没有引用的情况下将其复制到课堂上。现在是真正了解这一点的好时机——它会让你成为一个更好的人。

    【讨论】:

    • 这看起来是我想要的。引用 Commons 的话:“这个类可以为任何类构建一个好的 hashCode 方法。它遵循 Joshua Bloch 的《Effective Java》一书中规定的规则。编写一个好的 hashCode 方法实际上非常困难。这个类旨在简化过程。“我怀疑这会让我成为一个更好的人(尽管我关心作者的道德权利),但我希望它能让我成为一个更好的程序员......
    • 不幸的是,这些链接现在已经失效了。 :(
    【解决方案4】:

    如果您谈到为自定义类定义哈希码,最好的办法是定义所有字段哈希码函数的某种数学连接。

    在定义哈希码时,您的目标通常是尽量减少冲突,因此如果您执行此类操作,您通常会很清楚。

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

    【讨论】:

    • 你应该确保乘数是素数而不是非素数(尤其不是二的幂)
    • 这就是我所追求的。这些数字有什么神奇之处吗?
    • 感谢 AlBlue,乘数应该始终保持良好状态,以进一步减少碰撞次数
    【解决方案5】:

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

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

    说是a, b and c,那么你的hashCode实现应该是这样的

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

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

    【讨论】:

    • "哈希函数是内存地址" - 你确定吗?当一个对象被压缩时在内存中移动怎么办?
    • 尽可能合理地实用,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。) - java.sun.com/j2se/1.4.2/docs/api/java/lang/…
    • 我从 java 文档中得到它,它是默认实现,它意味着 new Object().hashCode() != new Object().hashCode()
    • 不,不同的对象可以具有相同的哈希码,这完全取决于那里的编码。 Object 的默认值非常简单,可能与创建对象的地址有关,但拥有 new String("abc").hashCode() == new String("abc") 绝对符合您的兴趣。哈希码()!
    • 再看一遍,我们并没有意见分歧。只是提炼信息。
    【解决方案6】:

    回答您的问题:如果您将函数生成的哈希码放入哈希表(字典/地图)中,则该哈希码将用于查找类实例的位置。如果你的哈希函数产生太多的冲突,你的哈希表的性能可能会像 O(n) 一样糟糕。

    【讨论】:

      【解决方案7】:

      这是我使用的哈希码组合函数(在 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;
      } 
      

      【讨论】:

        猜你喜欢
        • 2012-06-20
        • 2014-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-11
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多