【问题标题】:Do Dictionary.Keys.ToArray() and Dictionary.Values.ToArray() preserve ordering?Dictionary.Keys.ToArray() 和 Dictionary.Values.ToArray() 是否保留排序?
【发布时间】:2018-02-24 00:34:54
【问题描述】:

我有一本城市人口对象字典:

var cityPopulation = new new Dictionary<string, int>()
                    {
                        ["Sacramento"] = 495234,
                        ["Miami"] = 453579,
                        ["Memphis"] = 652717
                    });

我需要获得 2 个对应(相同顺序)的数组。我可以确定当我这样做时

var cities = cityPopulation.Keys.ToArray();
var populations = cityPopulation.Values.ToArray();

两个数组的顺序将被保留 - 即:

城市 - 第一个元素将是萨克拉门托、第二个 - 迈阿密和第三个 - 孟菲斯,并且对于人口数组 - 第一 - 495234、第二 - 453579 和第三 - 652717 (城市数组应该对应于人口数组)。

我如何确定这一点,如果不能确定,我该如何保持这些数组的顺序?

【问题讨论】:

  • Dictionary 不按顺序排列物品。您需要保持项目有序,还是只需要确保citiespopulations 在相同索引处具有对应的元素?
  • ToArray 肯定会保留顺序,但是字典没有任何顺序,因为它只是一个基于哈希的集合。
  • @Ryan - 我需要在相同的索引处有相应的元素(不是顺序)

标签: c# .net dictionary


【解决方案1】:

没有。 Dictionary&lt;TKey, TValue&gt; 不保留排序。

出于枚举的目的,字典中的每个项目都被视为一个KeyValuePair&lt;TKey, TValue&gt; 结构,表示一个值及其键。返回项目的顺序未定义。

来自 MSDN 上的Dictionary&lt;TKey, TValue&gt; Class

对于KeysValue 属性文档说它们的顺序没有指定,但两者之间是一致的:

Dictionary&lt;TKey, TValue&gt;.ValueCollection 中值的顺序未指定,但与Keys 属性返回的Dictionary&lt;TKey, TValue&gt;.KeyCollection 中关联键的顺序相同。

来自 MSDN 上的Dictionary&lt;TKey, TValue&gt;.Values Property

【讨论】:

    【解决方案2】:

    不,他们不“维护秩序”。或者,更准确地说,它们确实保留了顺序,但字典一开始就没有顺序。

    话虽如此,您的实际 要求似乎是生成两个数组,其中包含在字典中找到的项目并且彼此相关。这种安排称为parallel arrays

    要做到这一点,您只需要创建一个确定性排序的枚举(不管是什么,但关键是一个非常明显的候选者,因为它保证是唯一的)。一旦你有一个排序列表,只需将键和值选择到它们自己的数组中。

    var sortedPopulation = cityPopulation.OrderBy( a => a.Key );
    string[] cities = sortedPopulation.Select( a => a.Key ).ToArray();
    int[] populations = sortedPopulation.Select( a => a.Value ).ToArray();
    

    这应该为您提供所需的两个数组,其中元素对齐,即使它们不是“原始排序顺序”(从技术上讲甚至不存在)。

    完整示例:

    public static void Main()
    {
        var cityPopulation = new Dictionary<string, int>
        {
            {"Sacramento",495234 },
            {"Miami",     453579 },
            {"Memphis",   652717 }
        };
    
        var sortedPopulation = cityPopulation.OrderBy( a => a.Key );
        string[] cities = sortedPopulation.Select( a => a.Key ).ToArray();
        int[] populations = sortedPopulation.Select( a => a.Value ).ToArray();
    
        foreach (var c in cities)
            Console.WriteLine(c);
        foreach (var p in populations)
            Console.WriteLine(p);
    }
    

    输出:

    Memphis
    Miami
    Sacramento
    652717
    453579
    495234
    

    如果由于某种原因您想在不排序且不枚举原始列表两次的情况下获得结果,您也可以执行以下操作:

    public static void SplitDictionary<T1, T2>(Dictionary<T1, T2> input, out T1[] keys, out T2[] values)
    {
        keys = new T1[input.Count];
        values = new T2[input.Count];
        int i = 0;
    
        foreach ( var entry in input )
        {
            keys[i] = entry.Key;
            values[i++] = entry.Value;
        }
    }
    

    这是可行的,因为即使不能依赖字典的顺序,我们也会同时检索键和值。一旦它们被检索并放入一个数组中,它们的顺序就固定了。

    See my code on DotNetFiddle

    【讨论】:

    • 如果您投了反对票,也请发表评论,以便我改进答案。
    • 当您执行两次sortedPopulation.Select(...).ToArray() 时,您将收到警告 - “IEnumerable 可能存在多个枚举”。
    • 不,你没有。听起来你有 Resharper。 VS 不在乎,在同一个枚举上迭代两次绝对没有错。但为了完整起见,我添加了另一个避免该问题的代码示例。
    【解决方案3】:

    字典是不确定的。哈希表没有隐式排序。简而言之,不要依赖与添加值时相同的顺序。

    出于枚举的目的,字典中的每个项目都被视为 一个 KeyValuePair 结构,表示一个值及其 钥匙。返回项目的顺序未定义。

    You can read more here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多