在C#中迭代字典

var dict = new Dictionary<int, string>();
for (int i = 0; i < 200000; i++)
    dict[i] = "test " + i;

我使用下面的代码迭代这个字典:

foreach (var pair in dict)
    Console.WriteLine(pair.Value);

然后,我使用这个迭代它:

foreach (var key in dict.Keys)
    Console.WriteLine(dict[key]);

第二次迭代花费了大约3秒钟的时间。 我可以通过两种方法获得键和值。 我想知道的是,第二种方法是否有缺点。 由于我能找到关于这个问题的最受关注的问题不包括迭代字典的这种方式,我想知道为什么没有人使用它,以及它如何更快地工作。


你的时间测试有一些根本的缺陷:

  • Console.Writeline是一种I / O操作,它比内存访问和CPU计算花费更多时间。 迭代时间的任何差异都可能被这个操作的代价所拖垮。 就像在铸铁炉中测量便士的重量一样。
  • 你没有提及整个行动花了多长时间,所以说一个人比另一个少3秒就没有意义。 如果第一次运行需要300秒,第二次运行需要303秒,那么你就是微型优化。
  • 你没有提到你如何测量运行时间。 运行时间是否包含加载和引导程序集的时间?
  • 你没有提到可重复性:你是否多次运行这些操作? 几百次? 以不同的顺序?
  • 这是我的测试。 请注意,我尽我所能确保迭代方法是唯一发生变化的方法,并且包含一个控件,以查看纯粹由于for循环和赋值而占用了多少时间:

    void Main()
    {
        // Insert code here to set up your test: anything that you don't want to include as
        // part of the timed tests.
        var dict = new Dictionary<int, string>();
        for (int i = 0; i < 2000; i++)
            dict[i] = "test " + i;
        string s = null;
        var actions = new[]
        {
            new TimedAction("control", () => 
            {
        for (int i = 0; i < 2000; i++)
                s = "hi";
            }),
            new TimedAction("first", () => 
            {
                foreach (var pair in dict)
                s = pair.Value;
            }),
            new TimedAction("second", () => 
            {
                foreach (var key in dict.Keys)
                s = dict[key];
            })
        };
        TimeActions(100, // change this number as desired.
            actions);
    }
    
    
    #region timer helper methods
    // Define other methods and classes here
    public void TimeActions(int iterations, params TimedAction[] actions)
    {
        Stopwatch s = new Stopwatch();
        foreach(var action in actions)
        {
            var milliseconds = s.Time(action.Action, iterations);
            Console.WriteLine("{0}: {1}ms ", action.Message, milliseconds);
        }
    
    }
    
    public class TimedAction
    {
        public TimedAction(string message, Action action)
        {
            Message = message;
            Action = action;
        }
        public string Message {get;private set;}
        public Action Action {get;private set;}
    }
    
    public static class StopwatchExtensions
    {
        public static double Time(this Stopwatch sw, Action action, int iterations)
        {
            sw.Restart(); 
            for (int i = 0; i < iterations; i++)
            {
                action();
            }
            sw.Stop();
    
            return sw.Elapsed.TotalMilliseconds;
        }
    }
    #endregion
    

    结果

    控制:1.2173ms
    第一个:9.0233ms
    第二:18.1301ms

    所以在这些测试中,使用索引器的迭代次数大约是迭代键值对的两倍,这正是我期望的*。 如果我将条目数量和重复次数增加一个数量级,这保持大致相称,如果我按相反顺序运行两个测试,我会得到相同的结果。

    *为什么我会期待这个结果? Dictionary类可能在内部表示为KeyValuePairs,所以当你直接迭代它时,它真的需要做的一件事就是遍历它的数据结构,每次输入调用者时都要调用它。 如果你迭代键,它仍然需要找到每个KeyValuePair,并从中为你提供Key属性的值,这样,单独执行步骤的开销将大致与迭代它的开销相同。 然后你必须调用索引器,它必须计算提供的密钥的哈希值,跳转到正确的哈希表桶,然后对它在那里找到的任何KeyValuePairs的键进行相等性检查。 这些操作并不是非常昂贵,但是一旦你做了N次,它就像你重复遍历内部哈希表结构一样昂贵。

    链接地址: http://www.djcxy.com/p/30391.html

    上一篇: Iterating a dictionary in C#

    下一篇: using a for loop to iterate through a dictionary