【问题标题】:Point at which Dictionary lookup performs better than List?哪个 Dictionary 查找比 List 执行得更好?
【发布时间】:2014-04-05 08:40:16
【问题描述】:

l我们都知道,当我们想根据某个键值在集合中查找一项时,Dictionary/Hashset 等是 C# 中可用的最快选项。但是,显然它们依赖于设置存储桶并在用作查找参数的任何键值上调用哈希函数 - 这两者都有一些开销。

因此,按权利,这必须意味着 - 对于达到一定大小的集合 - 循环遍历列表/数组中的每个项目以“蛮力”寻找匹配项必须更快(即 List.Contains 方法)

http://www.dotnetperls.com/dictionary-time 上有一篇文章表明此阈值是 三个 项。坦率地说,我很惊讶字典用这么少的项目表现得更好!

我很好奇你们中是否有人做过自己的基准测试并可以验证这一点。我也对实例化 Dictionary 和 List 所需的时间感到好奇——上面的文章忽略了这一点(坦率地说,在大多数插入轻/阅读量大的情况下,我们会使用字典,因为它可能无关紧要——但是在某些情况下,这可能是决定使用哪个的重要因素。

另外:如果是这种情况(字典确实是比具有四个或更多值的列表更好的选择)那么为什么会这样呢?本文中的基准示例使用字符串键 - 默认字符串相等运算符/IEquatable 实现的性能成本是否比我意识到的要大得多? Dictionary 是否总是在查找期间调用键的 IEquatable 实现 - 还是仅在哈希冲突的情况下调用?

最后:如果键的类型是更简单的相等测试(如 Int32/Int64/Guid),这三个项目的阈值是否会有很大不同?

【问题讨论】:

  • 你做过基准测试吗?是的,字典总是必须调用 Equal 来确认,因为多个字符串可以(将)散列到相同的值。字典可以仅根据哈希来拒绝许多不匹配,这可能是它很快胜出的原因,但这也取决于访问模式。例如。如果最受追捧的项目位于列表的开头,则列表搜索可能会获胜。
  • 嗨“500” - 不,我自己没有做过任何基准测试。虽然这看起来有点背对前,但我想我可能会问这里是否有人在潜入自己之前做了一些非常彻底的基准测试(并在野外进行了讨论)。如果没有人打扰,而你们中的一些人认为这值得,那么我会做一些测试。

标签: c# list dictionary benchmarking hashset


【解决方案1】:

提供ListDictionary 类正是出于您提到的原因,它被描述为here,建议是:

推荐用于通常包含少于 10 个的集合 项目。

Microsoft 还提供HybridDictionary 描述here,让您两全其美。它描述了它的典型用法如下:

这个类推荐用于 字典不详。它利用了改进的性能 具有小集合的 ListDictionary,并提供了灵活性 切换到更好地处理更大集合的哈希表 比 ListDictionary。

对于您的具体情况,查看哪个表现最佳的唯一方法是基准测试

(请注意,以上示例仅供参考!使用新的 .NET 泛型集合通常会更好...)

【讨论】:

  • 嗨 Baldrick(顺便说一句好昵称):由于 ListDictionary 和 HybridDictionary 都在泛型之前 1.1 天来自 .Net - 使用引用类型键/值时的装箱成本肯定会抵消任何性能的很大一部分他们可能提供的改进?这本身当然是一个很好的问题......你们中的任何人在这个(后 .net 2.0)时代使用这些集合吗?
  • @Daniel Scott:谢谢!我并不完全推荐你使用它们——我只是从 MS 中提供了字典方法往往优于列表(即大约 10 个)的大小的证据,以及他们为获得更通用的解决方案而采取的方法(构建一个HybridDictionary 类)。值得一提的是,旧集合只有 box/unbox 具有值类型 - 对于引用类型,您只是 casting
  • ugh - 我今天一定是有些昏昏欲睡的一天 - 我的意思是说“使用值类型时”(而不是使用引用类型时)。但是,是的,你是对的 - 字符串键/某些引用类型值的 ListDictionary 应该表现得很好。我想我们中的许多人都遵循这样的教条,如此普遍地避免这些旧的非通用集合。
  • @DanielScott:一般来说,避免使用旧系列是一个好计划! :) 我已经在我的回答中添加了一个注释。
【解决方案2】:

您的文章可能没有详细介绍建立字典/列表的成本的原因是它在很大程度上是微不足道的。就此而言,如果您要在数据结构中进行一次查找,那么您如何实现它实际上并不重要,因为它将花费极少的时间。

我们关心的是访问,因为通常我们会多次访问数据结构,而这些重复访问的效果将大大超过设置时间的任何收益。

关于为什么即使项目很少,列表也会变慢:这是因为这不是列表的设计目的。这里的关键是计算通常比内存访问快得多。如果您正在寻找数据结构中的特定事物,那么拥有一种算法可以告诉您在哪里查找(散列函数),并且内存访问最少,可以让您显着加快速度。如果您需要按顺序访问项目(通常使用字符串),那么您需要一个列表。

【讨论】:

  • 您好丹尼尔,感谢您的回复。这是一个有趣的点(计算比数据访问更快)。我会认为虽然访问列表中的每个项目(按索引)的时间(这是一个非常小的数组包装器)应该非常快 - 所以迭代少数列表项目的开销应该是相当的最小。我的假设错了吗?或者,这是否归结为数据类型? (因为字符串是引用类型,获取列表项将涉及到数组中的内存地址)?
  • 如果它是一个小列表,访问每个项目确实会很快。但是计算一个简单的哈希函数然后进行一次访问通常仍然会更快。当您一遍又一遍地做任何一个时,差异就会变得明显。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多