当字典的值不确定时,为什么迭代字典的值以与插入相同的顺序返回?

时间:2019-11-25 19:32:31

标签: c# dictionary

众所周知,C#中的Dictionary在理论上不会保留插入元素的顺序。

  

出于枚举目的,字典中的每个项目都被视为代表值及其键的KeyValuePair<TKey,TValue>结构。物品的退还顺序是不确定的。

Source

但是,当我将随机键插入字典并在foreach循环中读取它们时,它们总是按插入顺序返回。

Random rand = new Random();
var map = new Dictionary<int, int>();
for (int i = 0; i < 10; ++i)
{
    var key = rand.Next(1000000000);
    var value = 0;

    Console.WriteLine($"Key: {key}");

    if (map.ContainsKey(key))
        continue;

    map.Add(key,value);
}

Console.WriteLine("Reading Back..");
foreach (var pair in map)
{
    Console.WriteLine($"Key: {pair.Key}");
}

此代码的输出如下:

Key: 593548784  
Key: 765023454  
Key: 1460074  
Key: 913286745  
Key: 804757753  
Key: 281489700  
Key: 395818098  
Key: 227086287  
Key: 323530161  
Key: 629618868

回读..

Key: 593548784  
Key: 765023454  
Key: 1460074  
Key: 913286745  
Key: 804757753  
Key: 281489700  
Key: 395818098  
Key: 227086287  
Key: 323530161  
Key: 629618868

我感到很惊讶,因为我以为当元素插入字典时,哈希键是通过取键字段的mod N来确定的,这意味着该顺序将丢失(除非N非常大)。

有人知道为什么这种行为吗?

2 个答案:

答案 0 :(得分:3)

Dictionary不支持排序的事实并不意味着您每次读取数据都会以随机顺序获得数据。通常,您将按照插入时的顺序对其进行遍历,但这是not guaranteed

  

出于枚举目的,字典中的每个项目都被视为代表值及其键的KeyValuePair结构。 退回商品的顺序不确定。

当您开始删除,插入和添加其他条目时,原始订单可能会丢失。您可以在this question的答案中找到更多详细信息。

这是删除项目的示例代码

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SO59038883
{
    internal class Program
    {
        static void Main()
        {
            // keep track of the key that have been inserted
            // so we can remove a random
            var trackedKeys = new List<int>();

            var rand = new Random();
            var map = new Dictionary<int, int>();
            for (var i = 0; i < 20; i++)
            {
                var key = rand.Next(1000000000);
                trackedKeys.Add(key);
                Console.WriteLine($"Key: {key}");

                if (map.ContainsKey(key))
                    continue;

                map.Add(key, i);

                // Let's remove three random keys
                // at set interval
                if (i == 5 || i == 10 || i == 15)
                {
                    // get a random key from our tracked key list
                    var index = rand.Next(trackedKeys.Count);
                    var keyToRemove = trackedKeys[index];
                    map.Remove(keyToRemove);
                }
            }

            Console.WriteLine("Reading Back..");
            foreach (var pair in map)
            {
                Console.WriteLine($"Key: {pair.Key} - Value: {pair.Value}");
            }

            if (Debugger.IsAttached)
            {
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
        }
    }
}

Output showing order once items have been removed.

答案 1 :(得分:2)

实现Dictionary的方式不会将项目直接存储到哈希表中,而是使用整数哈希表,该哈希表随后用于标识增长数组中的项目。如果没有从Dictionary中删除任何内容,则此数组将按添加顺序包含项目。从.NET开始,这种行为就一直是一致的。如果删除了项目,则将来的项目放置在数组中的顺序可能很难预测,我不希望它在所有.NET版本中都保持一致。