关于c#:字典的枚举器< TKey,TValue>

Does the Enumerator of a Dictionary<TKey, TValue> return key value pairs in the order they were added?

我知道字典不是有序的集合,不应该依赖于字典中插入和检索的顺序。

然而,这正是我注意到的:

  • 在字典中添加了20个键值对
  • 通过执行foreach(keyValuePair…)检索到它们

检索顺序与添加顺序相同。测试了大约16个键值对。

这是设计的吗?


这是巧合,尽管可以预见。你绝对不应该依赖它。通常在简单的情况下会发生这种情况,但是如果您开始删除元素并用相同的哈希代码替换它们,或者只是进入同一个bucket,那么该元素将占据原始元素的位置,尽管添加的时间比其他元素晚。

复制这个相对比较费劲,但我在不久前就设法做到了另一个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Collections.Generic;

class Test
{
    static void Main(string[] args)
    {
        var dict = new Dictionary<int, int>();        
        dict.Add(0, 0);
        dict.Add(1, 1);
        dict.Add(2, 2);
        dict.Remove(0);
        dict.Add(10, 10);

        foreach (var entry in dict)
        {
            Console.WriteLine(entry.Key);
        }
    }
}

结果显示10,1,2而不是1,2,10。

请注意,即使当前行为看起来总是按插入顺序生成元素,如果不执行任何删除操作,也不能保证将来的实现也会执行相同的操作…所以,即使在你知道不会删除任何内容的受限情况下,也不要依赖于此。


来自msdn:

For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair<(Of <(TKey, TValue>)>) structure representing a value and its key. The order in which the items are returned is undefined.

[添加强调]


如果要按固定顺序迭代字典,可以尝试ordereddictionary。


根据设计,Dictionary不是有序结构,因为它主要用于基于密钥的访问。

如果您需要按照特定的顺序检索项目,您应该查看Sorted Dictionary,它采用Comparer,用于对Sorted Dictionary中的键进行排序。


这是设计的吗?它可能不在原始的.NET Framework 2.0中,但现在有一个隐式契约,它们的顺序与添加的顺序相同,因为要更改它,将破坏太多依赖于原始通用字典行为的代码。与Go语言相比,它们的映射故意返回随机顺序,以防止映射用户依赖于任何顺序[1]。

框架编写者对DictionaryDocx1(7)所做的任何改进或更改都必须保持这种隐含的契约。

[1]"自从Go 1.0发布以来,运行时具有随机映射迭代顺序。",https://blog.golang.org/go-maps-in-action。


我相信枚举Dictionary将返回相同顺序的密钥,如果所有密钥哈希值都相同。这是因为Dictionary实现使用key对象的散列代码将key/value对插入bucket,并且这些值(通常)按插入的顺序存储在bucket中。如果您在用户定义的对象中一直看到这种行为,那么您可能没有(正确地)重写GetHashCode()方法?


我不这么认为,这本词典并没有授权对里面的条目进行内部排序。如果您还需要保持顺序,请使用附加的数据结构(数组或列表)和字典。