【问题标题】:Hash code as key in keyed collection哈希码作为键控集合中的键
【发布时间】:2014-06-03 22:35:24
【问题描述】:

据我(想)知道,Dictionary 被实现为一个哈希表,其中哈希码用于标识一个桶,然后搜索它的键。

在我看来,这意味着一个对象的哈希码在我的程序的单次运行期间保持稳定(松散地说)。

现在,这里

http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

我读过

“哈希码用于在基于哈希表的集合中进行有效插入和查找。哈希码不是永久值。因此: [...] 不要使用哈希码作为键从键控集合中检索对象。"

谁能给我解释一下这是什么意思?

【问题讨论】:

  • 嗯,这与其说是一个答案,不如说是一个观察结果,但obj1.HashCode() == obj2.HashCode() 并不暗示obj1.Equals(obj2)(除非您实现了Equals 方法来比较哈希码...)。
  • @Blam 也许我解释得不够清楚。我并不是说 HashCode 等同于 Equals。我说的是相反的——除非有人实现了他们的 Equals 来比较哈希码,否则它们是不同的。
  • @Tung 我看不出你的观察如何增加任何价值。
  • @Blam 也许是因为他的“观察”是文档想要表达的意思?

标签: c# .net hash


【解决方案1】:

当文档谈到“键控集合”时,它们与字典的含义不同。要深入了解它的实际含义,请注意实际上有一个 KeyedCollection 基类:http://msdn.microsoft.com/en-us/library/ms132438%28v=vs.110%29.aspx

关键段落是这样的:

与字典不同,KeyedCollection<TKey, TItem> 的元素不是键/值对;相反,整个元素都是值,而键嵌入在值中。例如,从 Visual Basic 中的KeyedCollection<String,String>(KeyedCollection(Of String, String) 派生的集合元素)可能是“John Doe Jr”。其中值为“John Doe Jr”。关键是“Doe”;或者包含整数键的员工记录集合可以从KeyedCollection<int,Employee> 派生。抽象的GetKeyForItem 方法从元素中提取键。

因此,键控集合是对象的集合,以及从每个对象中提取键的方法。从概念上讲,这类似于数据库中的表,您可以在其中定义一个主键,它是整个记录的子集。

因此,考虑到这一点,答案变得相对清晰。正如其他人所说,哈希码的平等并不意味着对象的平等。但是键控集合中的键(如数据库表中的主键)应该唯一标识确切的对象。所以哈希冲突的可能性使得它们不适合这个目的。

此外,即使在Dictionary 中,使用对象作为键和使用相同对象的哈希码作为键之间也存在重要区别。如果两个对象发生哈希冲突但不相等,Dictionary 仍会将它们存储为两个单独的键。这就是为什么覆盖 GetHashCode 以仅返回 1 总是有效的(尽管显然不利于性能)。作为示范:

var dict = new Dictionary<MyClass, string>();
var hashDict = new Dictionary<int, string>();

dict[myObj1] = "One";
hashDict[myObj1.GetHashCode()] = "One";
dict[myObj2] = "Two";
hashDict[myObj2.GetHashCode()] = "Two";

Console.Out.WriteLine(dict[myObj1]);  //Outputs "One"
Console.Out.WriteLine(hashDict[myObj1.GetHashCode()]); //Outputs "Two"

myObj1myObj2MyClass 的实例,它们具有相同的哈希码但不相等)

【讨论】:

  • 我取消删除我的答案以证明这是我的想法。但我不认为这是正确的。如果他们指的是 KeyedCollection,他们会说 KeyedCollection 而不是键控集合。 HashSet 和 Dictionary 都是键控集合的示例。一般来说,键控集合的键不应该是散列,这就是我认为他们所说的。密钥必须是唯一的。不保证哈希是唯一的。
  • @Blam 我希望 MS 能够保持他们的术语一致。我理解“键控集合”的方式是特定类型集合的概念,KeyedCollection 是实现该概念的类。您可以在此处使用短语“keyed collection”查看它们,例如:msdn.microsoft.com/en-us/library/dn169389%28v=vs.110%29.aspxmsdn.microsoft.com/en-us/library/5z658b67%28v=vs.110%29.aspx。在两者中,它都被专门用来指代具有嵌入键的项目集合,而不是例如。字典或哈希集。
  • 是的,我应该坚持我的回答。
【解决方案2】:

他们可能在谈论 KeyedCollection。
在这种情况下,使用哈希作为键是没有意义的。
它们的键应该是类使用的真实值。

enter link description here

和例子一样

public class SimpleOrder : KeyedCollection<int, OrderItem>
{
    // The parameterless constructor of the base class creates a  
    // KeyedCollection with an internal dictionary. For this code  
    // example, no other constructors are exposed. 
    // 
    public SimpleOrder() : base() {}

    // This is the only method that absolutely must be overridden, 
    // because without it the KeyedCollection cannot extract the 
    // keys from the items. The input parameter type is the  
    // second generic type argument, in this case OrderItem, and  
    // the return value type is the first generic type argument, 
    // in this case int. 
    // 
    protected override int GetKeyForItem(OrderItem item)
    {
        // In this example, the key is the part number. 
        return item.PartNumber;
    }
}

PartNumber 是 OrderItem 的一个属性(恰好是一个 int)
您永远不应该将 Hash OrderItem 用作 GetKeyForItem

【讨论】:

    【解决方案3】:

    我认为该特定项目的意思是不要使用哈希码作为键。例如,没有Dictionary&lt;int, MyObject&gt;,其中整数键是哈希码。

    主要原因是两个不同的项目可能具有相同的哈希码。

    使用哈希码的安全方法是……不要直接使用它们。也就是说,您很少会编写调用GetHashCode 的代码。如果您的代码没有调用GetHashCode,那么您的代码将无法保存这些值,并且您不会因为不应该依赖的东西而陷入麻烦。

    【讨论】:

      【解决方案4】:

      这解释了它:

      .NET Framework 不保证默认实现 GetHashCode 方法,该方法返回的值可能不同 .NET Framework 版本和平台之间,例如 32 位和 64 位平台。

      每次你在同一个环境下运行你的程序,你可能总是得到相同的hash码,但是如果你在不同的平台或者不同版本的.net框架上运行同一个程序,并不能保证hash代码将是相同的。

      【讨论】:

        【解决方案5】:

        文档意味着哈希码在程序的连续运行之间不能保证(甚至可能)相同。因此,如果您尝试将其用作外部 数据源(例如数据库或键值存储)的键,这将是不可靠的。然而,使用它作为存储桶表(在内存中就像在字典中)的索引的基础正是它的设计目的。

        【讨论】:

        • “存储桶表中索引的基础”但这与将其用作键不同,是吗?
        • @BenAaronson,对,它与将其用作外部密钥不同。我以为我说的很清楚,所以我不是很关注?
        • 我的意思不是外部集合的键,我的意思是它与将它用作程序中的字典的键不同。
        猜你喜欢
        • 2011-07-01
        • 2016-01-28
        • 2013-02-24
        • 1970-01-01
        • 2016-11-30
        • 2011-03-29
        • 1970-01-01
        • 2016-10-04
        • 2021-12-15
        相关资源
        最近更新 更多