【问题标题】:convert array to dictionary c# with int and string使用 int 和 string 将数组转换为字典 c#
【发布时间】:2012-07-17 13:07:52
【问题描述】:

我有一个包含字符串和字典的字符串数组。

String [] str_array;
Dictionary<int, string> dict = new Dictionary<int, string>();

dict = str_array.toDictionary();

如何将我的数组放入带有 int 和 String 的字典中? 我已经看过所有这些带有 bool 的示例,但我需要 int 和 string。

int(键)只是字典的实际位置(dict.count),值是该位置的数组值。

编辑:感谢所有答案,但我不想遍历数组。我假设使用 array.toDictionary 的性能会比遍历数组并将数组的值分配给字典要好。数组可能有 5k 个元素。

edit2:原因是我必须将字典传递给方法......它是必需的。我所有的值都在一个简单的数组中。

edit3:最重要的是性能。也许迭代数组并将值分配给 dict 比 array.toDictionary 更快,但问题是我没有那一小段代码来对两者进行基准测试。

【问题讨论】:

  • 尝试使用 for 循环遍历数组,然后使用循环中的索引作为键,将数组中的字符串值作为字典值,将循环内的项目添加到字典中。
  • toDictionary() 将始终创建您的 Dictionary 实例,因此您不需要一开始就对其进行初始化。
  • 我认为你必须为此编写一个扩展方法。这就是你要找的东西吗?
  • 您可以手动完成,只需遍历字符串数组,然后将每个字符串添加到字典中。使用一个 int 变量,每次通过循环都会递增作为键。
  • but i do not want to iterate over array. 你觉得ToDictionary 会用魔法吗?

标签: c# arrays dictionary


【解决方案1】:

首先 - 你的性能问题很有趣,并且带有过早优化的味道 - 正如这个答案将显示的那样,你可能会看到 for 循环和 ToDictionary 之间的几毫秒性能差异。

除非您在实时系统中运行它,否则我看不出有什么问题。

在节目中 - 以下是我能想到的三种(半)不同方式构建字典的粗略基准(只有真实世界的时间是可靠的)。第一个使用for 循环,第二个使用相同但不使用数组的Length 属性(只是为了感兴趣);第三次和第四次使用ToDictionary;一个使用Select,一个使用计数器变量(混合):

[TestMethod]
public void SomeBenchmark()
{
    List<double> forLoopTimes = new List<double>();
    List<double> forLoop2Times = new List<double>();
    List<double> toDictionaryTimes = new List<double>();
    List<double> hybridTimes = new List<double>();

    string[] array = Enumerable.Range(0, 5000).Select(i => i.ToString()).ToArray();

    Dictionary<int, string> dictionary;

    int runCount = 5000;
    int arrayLen = array.Length;

    while (runCount-- != 0)
    {
        Stopwatch sw = Stopwatch.StartNew();
        dictionary = new Dictionary<int, string>();
        for (int i = 0; i < array.Length; i++)
        {
            dictionary[i] = array[i];
        }
        sw.Stop();
        forLoopTimes.Add(sw.Elapsed.TotalMilliseconds);

        sw.Restart();
        dictionary = new Dictionary<int, string>();
        for (int i = 0; i < arrayLen; i++)
        {   //same as before - but using arrayLen instead of property
            dictionary[i] = array[i];
        }
        sw.Stop();
        forLoop2Times.Add(sw.Elapsed.TotalMilliseconds);

        sw.Restart();
        dictionary = array.Select((s, i) => new { Key = i, Value = s }).ToDictionary(v => v.Key, v => v.Value);
        sw.Stop();
        toDictionaryTimes.Add(sw.Elapsed.TotalMilliseconds);

        int counter = 0;
        sw.Restart();
        dictionary = array.ToDictionary(s => counter++, s => s);
        sw.Stop();
        hybridTimes.Add(sw.Elapsed.TotalMilliseconds);
    }
    Console.WriteLine("for loop average: {0} milliseconds", forLoopTimes.Average());
    Console.WriteLine("for loop(2) average: {0} milliseconds", forLoop2Times.Average());
    Console.WriteLine("ToDictionary average: {0} milliseconds", toDictionaryTimes.Average());
    Console.WriteLine("Hybrid average: {0} milliseconds", hybridTimes.Average());
}

结果(发布版本,在我的戴尔 2.4Ghz 工作站上运行大约需要 20 秒):

对于循环平均值:0.28880804 毫秒

For loop(2) 平均:0.2773845 毫秒

ToDictionary 平均:0.479094339999998 毫秒

混合平均:0.353655779999999 毫秒

所以for 循环无疑更快 - 至少是最接近的ToDictionary 实现的 22%。我已经尝试了 100,000 个元素,然后它达到了大约 30%。

注意第二个for 循环结果 - 似乎表明绕过Length 属性是个好主意。事实上,我已经连续运行了 4 次,结果如下(包括第一次,来自上面):

For循环:0.28880804、0.28562478、0.283770739999999、0.287241679999999

For循环(2):0.2773845、0.27621306、0.27869996、0.27962916

ToDictionary: 0.479094339999998, 0.476417939999997, 0.476162219999997, 0.475776479999997

混合:0.353655779999999、0.3583224、0.352022739999998、0.349865779999999

但是,我也看到了至少一个基准测试结果的结果——证明了这种基准测试在很大程度上是毫无意义的。实际上,我们也应该为每个测试生成不同的数组,以避免缓存等。

还有其他选择。

如果您调用的方法接受IDictionary&lt;int, string&gt;(注意 - 接口);而不是Dictionary&lt;int, string&gt;,您可以创建一个简单的包装器类型来实现接口的必要成员,从而完全避免投影到字典中的需要;只要只需要某些成员。这是一个几乎完整的实现:

public class FakeDictionary : IDictionary<int, string>
{
    private readonly string[] _array;

    public FakeDictionary(string[] array)
    {
        _array = array;
    }

    #region IDictionary<int,string> Members

    public void Add(int key, string value)
    {
        throw new NotSupportedException();
    }

    public bool ContainsKey(int key)
    {
        return key >= 0 && key < _array.Length;
    }

    public ICollection<int> Keys
    {
        get { return Enumerable.Range(0, _array.Length).ToArray(); }
    }

    public bool Remove(int key)
    {
        throw new NotSupportedException();
    }

    public bool TryGetValue(int key, out string value)
    {
        value = null;
        if (key >= 0 && key < _array.Length)
        {
            value = _array[key];
            return true;
        }
        return false;
    }

    public ICollection<string> Values
    {
        get { return _array; }
    }

    public string this[int key]
    {
        get
        {
            try
            {
                return _array[key];
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new KeyNotFoundException("Invalid key", ex);
            }
        }
        set //note - can't be used to add items
        {
            try
            {
                _array[key] = value;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new KeyNotFoundException("Invalid key", ex);
            }
        }
    }

    #endregion

    #region ICollection<KeyValuePair<int,string>> Members

    public void Add(KeyValuePair<int, string> item)
    {
        throw new NotSupportedException();
    }

    public void Clear()
    {
        throw new NotSupportedException();
    }

    public bool Contains(KeyValuePair<int, string> item)
    {
        return ContainsKey(item.Key) && _array[item.Key].Equals(item.Value);
    }

    public void CopyTo(KeyValuePair<int, string>[] array, int arrayIndex)
    {
        //too much for an SO answer.
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _array.Length; }
    }

    public bool IsReadOnly
    {
        //technically it's not - because we can modify individual elements - 
        //but at the collection-level it is
        get { return true; }
    }

    public bool Remove(KeyValuePair<int, string> item)
    {
        throw new NotSupportedException();
    }

    #endregion

    #region IEnumerable<KeyValuePair<int,string>> Members

    public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}

【讨论】:

    【解决方案2】:

    这可以满足您的需求,即使我没有意义使用仅是数组索引的键创建字典。

    Dictionary<int, string> dict = str_array
        .Select((s, i) => new { S = s, Index = i})
        .ToDictionary(x => x.Index, x => x.S);
    

    数组(或列表)的索引器(至少)与查找字典键一样快。

    https://stackoverflow.com/a/908055/284240

    【讨论】:

      【解决方案3】:

      最好只使用foreachfor,因为它更容易阅读并且比LINQ 具有良好的性能(我认为)。但这个答案只是为了提供另一种使用Enumerable.ToDictionary 的方式:-)

        Dictionary<int, string> dict;
        dict = Enumerable.Range(0, str_array.Length).ToDictionary(i => i, i => str_array[i]);
      

      还有一个简短的:

        int i = 0; // start key
        Dictionary<int, string> dict;
        dict = str_array.ToDictionary(s => ++i);
      

      【讨论】:

        【解决方案4】:

        很难想象你为什么需要这样的东西,因为你可以把一个数组想象成一个带有 int 键和字符串值的字典。

        但你可以这样做:

        int i = 0;
        foreach (string s in str_array)
        {
            dict.Add(i++, s);
        }
        

        【讨论】:

        • 我发现您鼓励不懂字典和数组的初学者程序员使用后增量有点令人惊讶!
        【解决方案5】:
        foreach(string s in str_array)
            dict.Add(dict.Count, s);
        

        类似的东西,作为一个简单的例子?

        或者,按照以下方式定义扩展方法:

        public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> source)
        {
            Dictionary<int, T> result = new Dictionary<int, T>();
            foreach(T item in source)
                result.Add(result.Count, item);
        }
        

        并通过调用str_array.ToDictionary()来使用它。

        【讨论】:

          【解决方案6】:

          如果你想让它变得简单,你可以使用扩展方法。

          public static class ExtensionMethod 
          {
              public static IDictionary<int, string> ToDictionary(this string[] array)
              {
                  return array
                      .Select((k, v) => new { Key = k, Value = v})
                      .ToDictionary(x => x.Key, x => x.Value);
              }
          }
          

          然后你就可以在你的程序中使用它了:

          String [] str_array;
          Dictionary<int, string> dict = new Dictionary<int, string>();
          
          dict = str_array.ToDictionary();
          

          请注意,只有当您遇到性能问题时,您才需要担心 LinQ 超过 foreach 的性能。只有大型操作的性能会有所不同。

          另见"Nested foreach" vs "lambda/linq query" performance(LINQ-to-Objects)

          【讨论】:

            【解决方案7】:
            for (int i = 0; i < str_array.Length; i++)
            {
                dict[i] = str_array[i];
            }
            

            【讨论】:

            • 什么是s?为什么ifor 之外声明?
            • s 已不复存在。你说得对,i 的范围应该更严格。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-06-20
            • 2013-06-07
            • 1970-01-01
            • 2017-09-21
            • 2011-04-06
            • 1970-01-01
            • 2013-01-08
            相关资源
            最近更新 更多