为什么c#dictionary foreach循环通常以与插入相同的顺序进行迭代

为什么c#dictionary foreach循环通常以与插入相同的顺序进行迭代,c#,dictionary,foreach,hashtable,C#,Dictionary,Foreach,Hashtable,根据共同的真理和许多经过投票的答案,C#字典在内部实现为哈希表,如果我有正确的理解,这意味着: foreach语句在字典项上的迭代顺序是不可预测的,因为在幕后,每个键都被散列,而底层数组中的索引都是根据该散列值分配的 如果我们想添加等于1,2,3,4,5,6,7的键,那么按什么顺序添加并不重要,因为与内部表示有关的唯一重要的事情是键的散列值 如果我以不同的顺序添加相同的键,那么在添加之后执行的foreach循环将以添加键的相同顺序迭代这些键,这怎么可能呢 这种行为背后有什么神奇的实现 我用于测试

根据共同的真理和许多经过投票的答案,C#
字典
在内部实现为
哈希表
,如果我有正确的理解,这意味着:

  • foreach
    语句在字典项上的迭代顺序是不可预测的,因为在幕后,每个键都被散列,而底层数组中的索引都是根据该散列值分配的

  • 如果我们想添加等于
    1,2,3,4,5,6,7的键,那么按什么顺序添加并不重要,因为与内部表示有关的唯一重要的事情是键的散列值

  • 如果我以不同的顺序添加相同的键,那么在添加之后执行的
    foreach
    循环将以添加键的相同顺序迭代这些键,这怎么可能呢

    这种行为背后有什么神奇的实现

    我用于测试的代码片段:

    static int[] x =  { 3, 9, 5, 11, 12, 13, 2, 24, 137, 4, 1256, 67, 1, 125555 };
    
    static void Main(string[] args)
    {
        //to avoid any rehashing and collisions issues the capacity is manually set to a value >> than the number of stored items
        Dictionary<int, string> dict = new Dictionary<int, string>(10000);
    
        for (int i = 0; i < x.Length; i++)
        {
            dict.Add(x[i], x[i].ToString());
        }
    
        Console.WriteLine("Foreaching element of dict after inserting from left to right\n");
    
        foreach (var kvp in dict)//this displays items in exactly same order as they were added
        {
            Console.WriteLine(kvp.Key);
        }
    
        dict.Clear();
    
        for (int i = x.Length - 1; i >= 0; i--)
        {
            dict.Add(x[i], x[i].ToString());
        }
    
        Console.WriteLine("Foreaching element of dict after inserting from right to left\n");
    
        foreach (var kvp in dict)//this displays items in exactly same order as they were added
        {
            Console.WriteLine(kvp.Key);
        }
    }
    
    static int[]x={3,9,5,11,12,13,2,24,137,41256,67,112555};
    静态void Main(字符串[]参数)
    {
    //为避免任何重新灰化和冲突问题,手动将容量设置为大于存储项目数的值>>
    Dictionary dict=新字典(10000);
    对于(int i=0;i=0;i--)
    {
    dict.Add(x[i],x[i].ToString());
    }
    Console.WriteLine(“从右向左插入后的dict的foreatching元素\n”);
    foreach(dict中的var-kvp)//这将以与添加项目完全相同的顺序显示项目
    {
    控制台写入线(kvp.Key);
    }
    }
    
    这个问题部分与:

    简言之,在内部,您可以在@Owen Pauling已经发布的源代码中查找的
    条目
    存储桶
    很少

    您应该最感兴趣的是
    private void Insert(TKey-key,TValue-value,bool-add)
    方法,它是为
    add
    调用的

    在你问题的上下文中,有趣的部分是索引的计算,在你的例子中,它归结为
    index=count。根据索引,将值添加到
    条目
    集合中,并为适当的铲斗箱设置一个值

    另一方面,在
    for
    循环期间,使用
    枚举器
    实现来初始化
    索引=0
    和(在简单的情况下)只是在
    MoveNext
    调用期间增加它。这意味着迭代是根据
    条目
    集合完成的。因此,您可以在迭代期间观察与插入期间相同的顺序

    如果有多个条目落入同一个bucket,那么流程可能会变得更复杂,但在您的示例中并非如此

    注意:

    请注意,上述描述仅适用于您正在介绍的案例。如果您尝试对字典进行一些修改,由于索引计算的流程略有不同,排序将被修改。例如,如果在显示关键点之前,您将:

    dict.Remove(12);
    dict.Add(15, "15");
    
    新添加的键将取代先前删除的键,因为在
    Insert
    方法中,您将陷入
    if(freeCount>0)
    状态


    此外,请记住不要依赖内部实现,因为除非明确记录,否则它可能会更改。

    您错了。哈希总是可以在正向(而不是反向)预测的。当前的实现具有这种副作用(但实现可以随时更改)。只要不删除项,它们将按添加顺序枚举。尝试删除一些项目,然后再添加一些。