我已经阅读了有关Dictionary / Hashset和其他无序收集订单问题的官方MSDN doc和多个SO问题。这个问题有点不同 - 我正在寻找实际的实例而不是理论
条件:
代码示例:
Dictionary<int, int> test = new Dictionary<int, int>();
for (int i = 0; i <= 100000; i++)
{
test.Add(i,i);
}
var c = 0;
while (c < test.Count)
{
if (c != test.ElementAt(c).Key)
{
Console.WriteLine("Mismatch!");
break;
}
c++;
}
Console.WriteLine("Test passed!");
//Program takes a 2hrs walk, does things, occasionally reads-only values from existing "test" collection
for (int i = 0; i <= 500000; i++)
{
test[test.Count] = test.Count;
}
c = 0;
while (c < test.Count)
{
if (c != test.ElementAt(c).Key)
{
Console.WriteLine("Mismatch!");
break;
}
c++;
}
Console.WriteLine("Test 2 passed!");
//repeat some stuff
问题: 根据上面的严格规则 - 当这种字典随机改变其元素的顺序时,有没有人遇到过这种情况?我们不是一次又一次地谈论MT Concurrent Collection,我对理论答案不感兴趣,I've read them.
test.ElementAt(5).key
返回key
11
或115
时的一个示例是我正在寻找的。 p>
答案 0 :(得分:1)
Dictionary<TKey, TValue>
的源代码可用here。如果您查看Enumerator.MoveNext()
,您会看到它按顺序遍历数组dictionary.entries
:
while ((uint)index < (uint)dictionary.count) {
if (dictionary.entries[index].hashCode >= 0) {
current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
index++;
return true;
}
index++;
}
如果您查看private void Insert(TKey key, TValue value, bool add)
(在添加和设置项目时都会调用),只要没有空闲条目(即没有删除任何内容),您就会看到 如果找不到密钥,则项目放在entries
数组的末尾;如果找到密钥,则放在当前位置:
private void Insert(TKey key, TValue value, bool add) {
// Snip
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
// Snip
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
// Key found. Set the new value at the old location in the entries array.
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
// Snip
}
// Key not found, add to the dictionary.
int index;
if (freeCount > 0) {
// Free entries exist because items were previously removed; recycle the position in the entries array.
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
// No free entries. Add to the end of the entries array, resizing if needed.
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}
// Set the key and value in entries array.
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
// Snip remainder
最后,如果你检查Resize()
,调用rehash字典的方法,你会看到entries
数组的顺序被保留:
Array.Copy(entries, 0, newEntries, 0, count);
因此,我们可以说使用此实现,只要没有删除,字典会保留添加键的顺序。
但这仅仅是一个实现细节。单声道(或某些未来.Net版本,例如.Net核心3.5或.Net完整5.2或其他)的Dictionary<TKey, TValue>
版本可以被重写,以便重新翻译字典改变顺序。 documentation唯一的承诺是返回项目的顺序是未定义的因此依赖其他任何东西是不明智的。