什么哈希算法.net使用?小丑怎么样?

时间:2009-05-21 18:43:54

标签: c# java hashtable

关于HashTable(及其后续衍生产品),有谁知道什么样的散列算法.net和Java使用?

列表和词典都是Hashtable的直接后果吗?

7 个答案:

答案 0 :(得分:6)

散列函数没有内置到散列表中;哈希表调用密钥对象上的方法来计算哈希值。因此,哈希函数根据密钥对象的类型而变化。

在Java中,List不是哈希表(即,它不扩展Map接口)。可以在内部实现带有哈希表的List(稀疏列表,其中列表索引是哈希表的关键),但是这样的实现不是标准Java库的一部分。

答案 1 :(得分:3)

我对.NET一无所知,但我会尝试代表Java。

在Java中,哈希码最终是给定对象的hashCode()函数返回的代码和HashMap / ConcurrentHashMap类中的辅助哈希函数的组合(有趣的是,两者使用不同的函数)。请注意,Hashtable和Dictionary(HashMap和AbstractMap的前身)是过时的类。列表实际上只是“别的东西”。

作为示例,String类通过将当前代码重复乘以31并添加下一个字符来构造哈希代码。有关详细信息,请参阅我在how the String hash function works上的文章。数字通常使用“自己”作为哈希码;其他课程,例如具有字段组合的Rectangle通常使用String技术的组合,乘以小素数并加入,但添加各种字段值。 (选择素数意味着您不可能在某些值和哈希码宽度之间得到“意外交互”,因为它们不会被任何东西划分。)

由于哈希表大小 - 即它所拥有的“桶”的数量 - 是2的幂,因此从哈希码中导出桶号,主要是通过删除最高位直到哈希码在范围内。辅助散列函数通过“扩展位”来防止所有或大部分随机性位于这些顶部位中的散列函数,以便一些随机性最终位于底部位并且不会被丢弃。如果没有这种混合,String哈希代码实际上可以很好地工作,但是用户创建的哈希代码可能效果不是很好。请注意,如果两个不同的哈希码解析为相同的桶号,则Java的HashMap实现使用“链接”技术 - 即它们在每个桶中创建条目的链接列表。因此,哈希码具有良好的随机性非常重要,因此项目不会聚集到特定范围的桶中。 (但是,即使有一个完美的哈希函数,你仍然可以通过平均法则预期会发生一些链接。)

哈希代码实现不应该是个谜。您可以查看您选择的任何类的hashCode()源。

答案 2 :(得分:2)

HASHING算法是用于确定HashTable中项目的哈希码的算法。

HASHTABLE算法(我认为是这个人所问的)是HashTable在给定哈希码时用来组织其元素的算法。

Java恰好使用chained哈希表算法。

答案 3 :(得分:2)

在我自己寻找相同的答案时,我在.net的参考源@ http://referencesource.microsoft.com中找到了这个答案。

     /*
      Implementation Notes:
      The generic Dictionary was copied from Hashtable's source - any bug 
      fixes here probably need to be made to the generic Dictionary as well.
      This Hashtable uses double hashing.  There are hashsize buckets in the 
      table, and each bucket can contain 0 or 1 element.  We a bit to mark
      whether there's been a collision when we inserted multiple elements
      (ie, an inserted item was hashed at least a second time and we probed 
      this bucket, but it was already in use).  Using the collision bit, we
      can terminate lookups & removes for elements that aren't in the hash
      table more quickly.  We steal the most significant bit from the hash code
      to store the collision bit.

      Our hash function is of the following form:

      h(key, n) = h1(key) + n*h2(key)

      where n is the number of times we've hit a collided bucket and rehashed
      (on this particular lookup).  Here are our hash functions:

      h1(key) = GetHash(key);  // default implementation calls key.GetHashCode();
      h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1));

      The h1 can return any number.  h2 must return a number between 1 and
      hashsize - 1 that is relatively prime to hashsize (not a problem if 
      hashsize is prime).  (Knuth's Art of Computer Programming, Vol. 3, p. 528-9)
      If this is true, then we are guaranteed to visit every bucket in exactly
      hashsize probes, since the least common multiple of hashsize and h2(key)
      will be hashsize * h2(key).  (This is the first number where adding h2 to
      h1 mod hashsize will be 0 and we will search the same bucket twice).

      We previously used a different h2(key, n) that was not constant.  That is a 
      horrifically bad idea, unless you can prove that series will never produce
      any identical numbers that overlap when you mod them by hashsize, for all
      subranges from i to i+hashsize, for all i.  It's not worth investigating,
      since there was no clear benefit from using that hash function, and it was
      broken.

      For efficiency reasons, we've implemented this by storing h1 and h2 in a 
      temporary, and setting a variable called seed equal to h1.  We do a probe,
      and if we collided, we simply add h2 to seed each time through the loop.

      A good test for h2() is to subclass Hashtable, provide your own implementation
      of GetHash() that returns a constant, then add many items to the hash table.
      Make sure Count equals the number of items you inserted.

      Note that when we remove an item from the hash table, we set the key
      equal to buckets, if there was a collision in this bucket.  Otherwise
      we'd either wipe out the collision bit, or we'd still have an item in
      the hash table.

       -- 
    */

答案 4 :(得分:1)

在.NET中任何声称是HashTable或类似内容的东西都没有实现自己的散列算法:它们总是调用对象被散列的GetHashCode()方法。

关于此方法执行或应该执行的操作存在很多混淆,尤其是涉及覆盖base Object implementation的用户定义或其他自定义类时。

答案 5 :(得分:1)

对于.NET,您可以使用Reflector查看各种算法。泛型和非泛型散列表有一个不同的,当然每个类定义自己的散列码公式。

答案 6 :(得分:1)

.NET Dictionary<T>类使用IEqualityComparer<T>计算密钥的哈希码,并在密钥之间执行比较以进行哈希查找。 如果在构造IEqualityComparer<T>实例时没有提供Dictionary<T>(它是构造函数的可选参数),它将为您创建一个默认实例,它使用object.GetHashCode和{{默认情况下为1}}方法。

至于标准object.Equals实施的工作原理,我不确定它是否有记录。对于特定类型,您可以在Reflector中读取方法的源代码,或尝试检查Rotor源代码以查看它是否存在。