使用int和string将数组转换为字典c#

时间:2012-07-17 13:07:53

标签: c# arrays dictionary

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

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

dict = str_array.toDictionary();

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

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

编辑:thx到所有答案,但我不想迭代数组。我假设使用array.toDictionary,性能优于迭代数组,只是将数组的值赋给字典。数组可能有5k个元素。

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

edit3:最重要的是效果。也许迭代数组并为dict赋值比array.toDictionary更快,但问题是我没有那么一小段代码可以对两者进行基准测试。

7 个答案:

答案 0 :(得分:8)

首先 - 你的性能问题很有趣并且过早优化 - 正如这个答案所示,你可能会看到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毫秒

     

对于循环(2)平均值:0.2773845毫秒

     

ToDictionary平均值:0.479094339999998毫秒

     

混合平均值:0.353655779999999毫秒

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

注意第二个for循环结果 - 似乎表明绕过Length属性是一个好主意。事实上,我连续完成了4次运行,这些结果(包括第一次,从上面开始):

  

For loop:0.28880804,0.28562478,0.283770739999999,0.287241679999999

     

For loop(2):0.2773845,0.27621306,0.27869996,0.27962916

     

ToDictionary:0.479094339999998,0.476417939999997,0.476162219999997,0.475776479999997

     

Hybrid:0.353655779999999,0.3583224,0.352022739999998,0.349865779999999

然而,我已经看到结果至少有一个基准测试结果翻转 - 证明了这种基准测试的无可争议性。实际上,我们应该为每个测试生成不同的数组,以避免缓存等。

还有另一种选择。

如果您正在呼叫的方法接受IDictionary<int, string>(注意 - 界面);而不是Dictionary<int, string>你可以创建一个简单的包装器类型来实现接口的必要成员,从而避免了将项目投射到字典中的需要;只要只需要某些成员。这是一个几乎完整的实现:

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
}

答案 1 :(得分:4)

这就是你想要的,即使我用一个只是一个数组索引的键来创建一个Dictionary是没有意义的。

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

答案 2 :(得分:2)

最好只使用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);

答案 3 :(得分:1)

for (int i = 0; i < str_array.Length; i++)
{
    dict[i] = str_array[i];
}

答案 4 :(得分:1)

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

但你可以这样做:

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

答案 5 :(得分:1)

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 :(得分:1)

如果您希望简化,可以使用扩展方法。

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)