这里有很多好主意,但很分散,所以我会尝试创建一个更好的答案,即使问题已经得到解答。
首先,Dictionary 没有保证顺序,因此您仅使用它来快速查找键并找到对应的值,或者您枚举所有键值对而不关心顺序是什么。
如果您想要订购,则使用 OrderedDictionary,但代价是查找速度较慢,因此如果您不需要订购,请不要要求。
字典(和 Java 中的 HashMap)使用散列。无论您的表格大小如何,这都是 O(1) 时间。有序字典通常使用某种平衡树,即 O(log2(n)),因此随着数据的增长,访问速度会变慢。比较一下,对于 100 万个元素,大约是 2^20,所以你必须对一棵树进行大约 20 次查找,但对于哈希映射需要 1 次。这要快很多。
散列是确定性的。非确定性意味着当你第一次散列(5),下一次你散列(5)时,你会得到一个不同的地方。那是完全没用的。
人们的意思是,如果您将内容添加到字典中,则顺序很复杂,并且在您添加(或可能删除)元素时随时更改。例如,假设哈希表中有 50 万个元素,而您有 40 万个值。当您再添加一个时,您将达到临界阈值,因为它需要大约 20% 的空白空间才能有效,因此它分配了一个更大的表(例如,100 万个条目)并重新散列所有值。现在他们都在与以前不同的位置。
如果您两次构建相同的字典(仔细阅读我的声明,THE SAME),您将获得相同的顺序。但正如乔恩正确所说,不要指望它。太多的东西可以使它不一样,即使是最初分配的大小。
这提出了一个很好的观点。必须调整哈希图的大小真的非常昂贵。这意味着您必须分配一个更大的表,并重新插入每个键值对。因此,非常值得分配 10 倍所需的内存,而不是必须进行一次增长。了解您的 hashmap 大小,并尽可能预先分配足够的空间,这是一个巨大的性能优势。而且,如果您有一个无法调整大小的糟糕实现,那么如果您选择的尺寸太小,那将是一场灾难。
现在 Jon 在我的评论中与我争论的问题是,如果您在两次不同的运行中将对象添加到 Dictionary 中,您将获得两种不同的排序。没错,但这不是字典的错。
当你说:
new Foo();
您正在内存中的新位置创建一个新对象。
如果你使用值 Foo 作为字典中的键,没有其他信息,他们唯一能做的就是使用对象的地址作为键。
也就是说
var f1 = new Foo(1);
var f2 = new Foo(1);
f1 和 f2 不是同一个对象,即使它们具有相同的值。
因此,如果您要将它们放入字典中:
var test = new Dictionary<Foo, string>();
test.Add(f1, "zero");
不要指望它与以下内容相同:
var test = new Dictionary<Foo, string>();
test.Add(f2, "zero");
即使 f1 和 f2 具有相同的值。这与 Dictionary 的确定性行为无关。
散列是计算机科学中一个很棒的主题,我最喜欢在数据结构中教授。
查看 Cormen 和 Leiserson,了解有关红黑树与散列的高端书籍
这个叫 Bob 的人有一个很棒的关于散列和最佳散列的网站:http://burtleburtle.net/bob