C#字典的效率


14

C#字典是查找某些东西是否存在等的简单方法。但是我对它们的工作方式有疑问。假设不是字典,而是使用ArrayList。我没有使用ContainsKey(或使用另一种语言的等效方法)循环遍历ArrayList来检查那里是否存在某些东西(或者如果对数据进行排序或类似的操作,则执行二进制搜索)。效率有何不同?该ContainsKey方法是否使用某种更有效的方法,而不是遍历键并检查我搜索的内容是否存在?

如果说我创建了一个特定的哈希函数,该哈希函数与我拥有的数据类型相对应,并且是专门针对该数据集而设计的,那么是的,该哈希函数的确比循环遍历数据的速度更快。但是字典是通用的。ContainsKey方法不是特定于它获取的数据,它是一种常规的搜索方法。

基本上我要问的是。词典对程序员很有帮助。它们包括对许多事情有帮助的方法,并且它们将字符串与整数,(键和值)等结合在一起。但是关于效率,他们提供了什么?什么是在具有的差异dictionaryVS一个ArrayListstructs(string,int)


您实际上是在将苹果与橙子进行比较。我认为你正在寻找的关键词是Data Structures 本wiki链接可能是给你更多的帮助
Ampt模型

Answers:


22

你得挖了一下,看看该怎么解释在C#中实现-它不是那样明显的HashMap(哈希表)或TreeMap的(一个排序树)(或ConcurrentSkipListMap -一个跳跃列表)。

如果您深入到“备注”部分:

Dictionary泛型类提供了从一组键到一组值的映射。字典的每个加法项都包含一个值及其关联的键。通过使用键的值检索值非常快,接近O(1),因为Dictionary类是作为哈希表实现的。

我们终于得到它了。这是一个哈希表。请注意,我已经在此处链接了Wikipedia文章-相当不错的阅读。您可能希望阅读有关冲突解决的部分。可能会获得一个病理数据集,其中查找会演变为O(N)(例如,由于某种原因,您插入的所有内容都落入哈希表中的相同哈希值或索引,并且剩下线性探测)。

虽然Dictionary是通用解决方案,但您不应该传递具体的类型(例如Dictionary),而应该传递接口。在这种情况下,该接口为IDictionarydocs)。为此,您完全有能力编写自己的字典实现,该实现对所拥有的数据进行最佳处理。

至于各种查找/包含的效率?

  • 走未排序的清单:O(N)
  • 排序数组的二进制搜索:O(log N)
  • 排序树:O(log N)
  • 哈希表:O(1)

对于大多数人来说,哈希表就是他们想要的。

您可能会发现SortedDictionary是您想要的:

SortedDictionary<TKey, TValue>通用类是O(log n)的检索,其中n是字典中的元件的数目的二进制搜索树。在这方面,它类似于SortedList<TKey, TValue>泛型类。这两个类具有相似的对象模型,并且都具有O(log n)检索。

同样,如果数据结构不是理想地适合您的数据的结构,则会为您提供工具(接口),以便能够编写最适合您数据的结构。

字典本身是一种抽象数据类型。您给了我一本字典,我知道我可以用它做些什么,那里的所有工具供我使用,因为它就是字典。如果您给我一个ArrayList,我会发现自己编写了自己的代码来搜索,插入或删除列表中的项目。这浪费了我的时间,也意味着当我一次又一次地复制代码时,出现错误的可能性更大。


5
O(1)不一定“快速”。对于应用程序正在处理的集合大小,遍历列表仍然比散列表快。
whatsisname 2014年

5
@whatsisname绝对不能说O(1)是快速的。它肯定有可能成为最快的。遍历哈希表的键比ArrayList的键慢(除非您使用Java提供的LinkedHashMap之类的东西)。重要的是要了解您的数据及其行为方式,并为它选择合适的集合-如果不存在,则编写它。当然,假设这样的努力确实值得时间(首先是个人资料!)。

您的引言说:“通过使用键的键检索值非常快,接近O(1),因为Dictionary类是作为哈希表实现的。”,因此OP可能会使这两个概念混淆。换句话说,我想说明的是,大O并不能说明“速度”的全部故事。
whatsisname 2014年

3
直接来自Microsoft的@whatsisname。除非您具有病理性哈希表(通过某种其他机制解决哈希冲突),否则使用键查找值的速度将比在树或排序列表(或未排序列表)中查找键更快。例如,Java使用线性探测(步骤1)来解决冲突- 在表太满或散列冲突太多的情况下,速度可能会变慢。对于一般情况,这已经足够了。

作为一个相关示例,我最近在c ++中优化了一些代码,该代码最初使用哈希表来存储大约20个条目的数据集,并且需要大约400ms的时间来完成。切换到二叉树可将其降低到200ms,因为该树更易于访问。但是我可以通过使用名称值对数组和启发式查找功能(根据过去的访问模式来猜测从何处开始查找)来进一步削减它。因此,这完全取决于访问中有多少数据以及访问中存在哪种模式(例如本地性)。
Jules 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.