使用KeyValuePair作为键的字典的糟糕表现(C#.NET)

时间:2015-11-13 15:27:17

标签: c# .net dictionary

在我写的应用程序中,我有两个可能很大的数据集,我需要相互映射。一个是从Web服务返回的List,一个是DataTable。我需要为列表中的每个项目取ANSI(或ISO)编号,并找到包含该ANSI编号的DataTable行,然后用它做一些事情。

由于DataTable.Select非常慢,而且我必须为List中的每个项目执行此操作,因此我尝试了更快的替代方案。请记住,DataTable对象没有数据库。所以我无法利用任何SQL功能或类似的东西。

我认为最快的方法可能是使用KeyValuePair(A:Ansi编号或I:Iso编号)创建字典并将其用作密钥。该值将是行的其余部分。创建该字典显然需要一点处理时间,但随后我可以利用字典的极快搜索时间来查找我需要的每一行,然后将行添加回表中。因此,在列表的foreach循环中,我只会使用字典而不是O(n)或DataTable.Select所具有的O(1)的复杂性。

令我惊讶的是,这本字典非常慢。直到我发现使用字符串(只是ANSI编号)而不是KeyValuePair才能大大提高性能,我无法找出原因。我的谈话速度提高了数百倍。这有可能在地球上如何?以下是我的测试方法:

我生成一个模拟Web服务输出的List。我使用键(字符串或KeyValuePair)和DataRow作为值创建基于该列表的字典。我浏览了该列表的foreach循环,并在我的字典中搜索该列表中的每个项目,然后为返回的DataRow分配一个值。那就是它。

如果我使用KeyValuePair作为访问字典的密钥,则需要几秒钟来处理1,000个项目,如果我修改字典只将字符串作为密钥,则10,000个项目需要几毫秒。仅供参考:我设计了测试以便始终有命中,因此始终可以找到所有键。

以下是我测量时间的代码块:

foreach(ProductList.Products item in pList.Output.Products)
{
   //KeyValuePair<string, string> kv = new KeyValuePair<string, string>("A", item.Ansi);
   DataRow row = dict[item.Ansi];
   for (int i = 0; i < 10; i++)
   {
      row["Material"] = item.Material + "a"; //Do stuff just for debugging
   }
   hits++;
}

那么,如果我使用Dictionary(KeyValuePair,DataRow)而不是Dictionary(String,DataRow),那么地球上的执行时间会突然变长几百倍呢?

2 个答案:

答案 0 :(得分:9)

KeyValuePair<TKey, TValue>未实施GetHashCode()方法。这意味着,意味着组织字典的唯一方法已经消失,而且你的线性搜索效率低下。

这应该不足为奇,因为它不是KeyValuePair<TKey, TValue>的设计目标 - 它是字典使用的内部结构,而不是密钥。 .NET对象不需要有用的密钥,并且从0个调用中返回GetHashCode()是完全有效的。

如果您不想使用自己的结构,请使用Tuple。但我真的只是为任何类型的持久性创建自己的结构,真的。

作为旁注,DataTable.Select实际上非常快,它的设计目的是 - 过滤输出数据。它并不是真的被设计为在循环中被调用了数百次 - 但开销占主导地位。当然,这假设你有适当的指数。在您的情况下,我认为每次调用Select时都会重新生成索引,这有点慢:)

答案 1 :(得分:1)

您可能会获得大量与键值对的哈希冲突。您可以使用GetHashCode进行测试。

下面的链接是元组,但我非常怀疑你有关键值对的事情是一样的。 gethashcode-high-rate-of-duplicates我会将其标记为重复,但很多人还有其他内容。

在此link中,Microsoft建议不要为键使用值类型。 KVP的GetHashCode继承自值类型。