C# Dictionary not adding new item at last index after remove the same key?
当我从字典中删除一个键,然后我想使用相同的键添加,但新添加的键不在字典的最后一个索引中时,我发现了这个行为。
1 2 3 4 5 6 7 8 9 | Dictionary<string, byte> test = new Dictionary<string, byte>(); test.Add("c", 1); // [{"c", 1}] test.Add("b", 2); // [{"c", 1}, {"b", 2}] test.Add("a", 3); // [{"c", 1}, {"b", 2}, {"a", 3}] test.Remove("b"); // [{"c", 1}, {"a", 3}] test.Add("b", 2); // [{"c", 1}, {"b", 2}, {"a", 3}] <= why this happen? // [{"c", 1}, {"a", 3}, {"b", 2}] and not this? |
我能知道为什么吗?我如何才能使新添加的键成为字典的最后一个索引呢?
字典是哈希表。如果查看哈希表的定义,您会发现哈希表是无序的。
我看了.NET字典实现的具体细节已经有一段时间了,所以在我的故事的其余部分可能会有一些错误——但这是我从细节中记得的:
实现哈希表有很多不同的方案,但是.NET使用的方案类似于带有一些变体的"开放寻址"算法。基本上,新项目被添加到一个列表中(在末尾),哈希表(一个静态数组)在这个列表中添加指针。这就是为什么它实际上似乎保留了秩序。
在某些时候,由于修改或增长,数据将被"垃圾"填满。在这一点上,实施将做一个重新粉饰。如果我记得正确的话,这也是一个点,它将检查是否有太多的碰撞——如果是这样,它将使用一个随机素数将所有哈希值相乘(从而减少碰撞的次数)。真的很优雅。
由于开放寻址方案指向列表中的元素,因此列表中的顺序并不重要。当你列举一本字典时,你基本上看一下这个列表。
您可能想知道为什么它没有枚举哈希代码数组。好吧,哈希表通常分配过度,数据无论如何都存储在另一个列表中。这就意味着这种替代方案的效率要低得多。如果您要枚举哈希表,您可能也会得到一个更一致的结果——但是由于冲突,仍然不会得到一个完全一致的结果。(例如,如果A和B在同一哈希代码上,插入顺序将决定A是跟随B还是反之亦然)。
如果您正在寻找像"set union"这样需要一致排序的算法,我建议使用像
您可以在这里看到dictionary类的实现代码
如您所见,该实现使用一种跟踪条目数组中自由位置列表的技术,当添加新值时,首先使用自由条目。
框架中有一个非泛型的listDictionary类,我相信它总是在列表的末尾添加新项。请记住,对IDictionary实现的访问通常平均为O(N),而对您当前使用的通用字典的访问平均为O(1)。