可接受的类型,用作HashTable中的键

时间:2009-11-03 20:39:52

标签: language-agnostic types hashtable key

我必须承认对HashTables的工作原理只有一个基本的理解,尽管从我知道的很少,它看起来相当简单。我的问题是这样的:似乎传统的智慧是使用简单的基本值类型,例如HashTable中的键的整数。但是,也经常使用字符串,即使在许多语言中它们都被实现为引用类型。我觉得通常鼓励使用复杂的引用类型;我猜这是因为这样做需要更慢的哈希函数?但那么为什么字符串如此常用呢?毕竟,char []数组内部不是一个字符串(在大多数语言中也是如此)?

最后,哪些值类型通常被视为在HashTable中用作键的“最佳”(甚至简单“可接受”)选项?是否有任何常用的选择实际上被视为“坏”(可能像字符串一样)?

5 个答案:

答案 0 :(得分:5)

这不是字符串整数或价值与参考的关系,而是可变密钥与不可变密钥的关系。只要密钥是不可变的(因此它们的哈希值永远不会改变)它们可以索引哈希表。例如,Java中的字符串是不可变的,因此非常适合作为哈希表键。

顺便说一句,如果数据类型足够简单,总是按值传递(比如标量),那么它当然可以。

但现在想象你使用的是一种可变类型;如果你给我一个这些对象中的一个作为键的引用,我将计算它的哈希值,然后将它放在我的一个哈希表桶中。但是当你以后修改对象时,我将无法得到通知;并且该对象现在可能位于错误的桶中(如果其哈希值不同)。

希望这有帮助。

答案 1 :(得分:4)

大多数字符串实现虽然它们可能在托管环境中显示为引用类型,但它们的实现通常是不可变类型。

散列函数的作用是将大量状态映射到较少数量的状态。

这就是为什么字符串散列有助于测试字符串相等性。您可以将值映射到数组的索引,并非常快速地查找有关该值的一些信息。您不需要将每个字符与每个其他字符串中的每个其他字符进行比较。你可以对任何事情说同样的事情。所有这些都是以某种有用的方式减少或指纹识别任意数量的字节。

这是关于您在哈希表中使用的键类型的讨论变得无效的原因,因为它是将该值映射到较小的状态空间以及如何在内部使用它使其有用。整数通常是硬件友好的,但32位并不是一个很大的空间,并且在任意输入的空间内可能发生冲突。

最后,当您使用哈希表时,计算哈希值的成本与将每个值与每个其他可能位置中的每个其他值进行比较所花费的时间相比是无关紧要的(假设您的哈希表)包含数百个项目。)

答案 2 :(得分:3)

只要提供合适的散列函数,所有类型都将作为键。记住,哈希表只是一个线性数组。哈希函数接受某种类型的密钥,并计算存储值的哈希表数组(称为存储桶)中的索引(虽然存在一些冲突问题)。

所以真正棘手的部分是找到一个哈希函数。当然它应该具有某些属性,如计算简单,混乱(几乎相同的键应映射到完全不同的散列表桶),确定性(相同的键意味着相同的散列表桶),均匀性(所有可能的键均匀映射到(桶),或者说是主观性(应该使用哈希表的所有桶)。

似乎更容易为整数这样的简单类型定义这样的函数。

答案 3 :(得分:1)

最好的hash keys是那些

  1. 有良好的(如低collisions)哈希(请参阅Object.GetHashCode for .NET,Object.hashcode for Java)
  2. 进行快速比较(适用于存在哈希冲突的情况)。
  3. 所有这一切,我认为Strings在大多数情况下都是很好的哈希键,因为Strings有许多优秀的哈希实现。

答案 4 :(得分:1)

如果您使用复杂类型作为键,那么:

  • 哈希表实现很难将项目分组到桶中以便快速检索;它将如何决定如何将一系列哈希分组到一个桶中?
  • 哈希表可能需要对类型有深入了解才能选择存储桶。
  • 存在对象属性发生变化的风险,导致项目以错误的桶结束。哈希必须是不可变的。

常用的整数,因为它们很容易拆分成与桶对应的范围,它们是值类型,因此是不可变的,并且它们很容易生成。