为什么.Net词典中的条目按添加顺序排列?
我刚刚看到了这种行为,对此感到有些惊讶...
如果我将3或者4个元素添加到字典中,然后执行"对于每个"来获取所有键,则它们的显示顺序与我添加它们的顺序相同。
这让我感到惊讶的原因是,字典在内部应该是HashTable,所以我希望事情以任何顺序出现(按键的哈希排序,对吗?)
我在这里想念什么?
这是我可以指望的行为吗?
编辑:好的,我已经想到了发生这种情况的许多原因(例如条目的单独列表,这是否是巧合,等等)。
我的问题是,有人知道这是如何工作的吗?
解决方案
字典按哈希顺序检索项目。它们以插入顺序出现的事实完全是巧合。
MSDN文档说:
The order of the keys in the KeyCollection is unspecified, but it is the same order as the associated values in the ValueCollection returned by the Values property.
据我所知,这不应该是一种可依赖的行为。要快速检查它,请使用相同的元素并更改将它们添加到字典的顺序。我们会看到是否按添加顺序将它们找回,或者这只是一个巧合。
来自MSDN的报价:
The order of the keys in the Dictionary<(Of <(TKey, TValue>)>).KeyCollection is unspecified, but it is the same order as the associated values in the Dictionary<(Of <(TKey, TValue>)>).ValueCollection returned by the Dictionary<(Of <(TKey, TValue>)>).Values property.
我们在测试中添加了哪些键,并且以什么顺序添加?
我们不能指望这种行为,但这不足为奇。
考虑如何为简单的哈希表实现键迭代。我们将需要遍历所有哈希存储桶,无论它们中是否包含任何内容。从大哈希表中获取小数据集可能效率不高。
因此,保留一个单独的重复键列表可能是一个很好的优化。使用双向链接列表,我们仍然可以获得固定时间的插入/删除。 (我们将使指针从哈希表存储桶中返回到该列表。)以这种方式遍历键列表仅取决于条目数,而不取决于存储桶数。
我认为这来自于旧的.NET 1.1时代,我们有两种字典" ListDictionary"和" HybridDictionary"。 ListDictionary是在内部实现为有序列表的字典,推荐用于"少量条目"。然后,我们有了HybridDictionary,它最初在内部被组织为一个列表,但是如果它变得大于可配置的阈值,它将成为一个哈希表。这样做是因为历史上适当的基于散列的字典被认为是昂贵的。现在已经没有多大意义了,但是我想.NET只是基于旧的HybridDictionary的新Dictionary泛型类。
注意:无论如何,正如其他人已经指出的那样,我们绝对不要指望字典顺序
条目可能全部都在字典的同一哈希存储桶中。每个存储桶可能是存储桶中的条目列表。这将解释按顺序返回的条目。
达到特定列表大小时,仅检查每个条目而不是散列会更便宜。那可能就是正在发生的事情。
添加100或者1000个项目,然后查看它们是否仍处于相同的顺序。
这个问题和许多答案似乎误解了哈希表或者字典的目的。这些数据结构对于包含在数据结构中的项目的值(实际上是键)的枚举没有指定的行为。
字典或者哈希表的目的是能够有效地查找给定已知键的特定值。任何字典或者哈希表的内部实现都应在查找中提供这种效率,但不必提供有关值或者键的枚举或者"对于每个"类型迭代的任何特定行为。
简而言之,内部数据结构可以按其希望的任何方式(包括它们的插入顺序)存储和枚举这些值。
如果在3.5类库上使用.NET Reflector,则可以看到Dictionary的实现实际上将项目存储在数组中(根据需要调整大小),并将索引散列到该数组中。获取键时,它会完全忽略哈希表,并遍历项数组。因此,由于在数组末尾添加了新项,因此我们将看到描述的行为。看起来,如果我们执行以下操作:
add 1 add 2 add 3 add 4 remove 2 add 5
我们会回来1 5 3 4,因为它会重复使用空插槽。
重要的是要注意,就像许多其他软件一样,我们不能指望将来(或者过去)的版本中存在这种行为。如果要对字典进行排序,则可以使用SortedDictionary类来实现此目的。
我讨厌这种"按设计"功能。我认为,在给班级起一个通用名称" Dictionary"时,它也应该表现得"与一般预期一样"。例如,std :: map始终对其键值进行排序。
编辑:显然,解决方案是使用SortedDictionary,其行为类似于std :: map。