1

I have a string array with strings and a dictionary.

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

dict = str_array.toDictionary();

How do I get my array into a dictionary with int and String? I have seen all those examples with bool, but I need int and string.

The int (key) would be just the actual position of the dictionary (dict.count) and the value would be the value of the array at that position.

edit: thx to all answers but I do not want to iterate over array. I assume that with array.toDictionary the performance would be better than iterating over array and just assigning the value of the array to a dictionary. Arrays might have 5k elements.

edit2: the reason is that i have to pass a dictionary to a method...its REQUIRED. and all my values are within a simple array.

edit3: The most important thing is performance. Maybe iterating over array and assigning values to dict is faster than array.toDictionary, but the problem is that i do not have that little piece of code to benchmark both.

4

7 回答 7

8

首先-您的性能问题很有趣,并且带有过早优化的味道-正如此答案所示,您可能正在查看for循环和ToDictionary.

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

进入节目 - 以下是我能想到的三种(半)不同方式来构建字典的粗略基准(只有真实世界的时间是可靠的)。第一个使用for循环,第二个做同样的事情但不使用数组的Length属性(只是为了感兴趣);第三和第四次使用ToDictionary;一个使用 a 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

对于循环(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<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
}
于 2012-07-17T13:11:44.420 回答
4

这可以满足您的需求,即使对我来说创建一个带有仅是数组索引的键的字典也没有意义。

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

于 2012-07-17T13:13:45.417 回答
2

最好只使用foreach或者for因为它更容易阅读并且在 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);
于 2012-07-17T13:26:24.773 回答
1
for (int i = 0; i < str_array.Length; i++)
{
    dict[i] = str_array[i];
}
于 2012-07-17T13:10:17.600 回答
1

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

但你可以这样做:

int i = 0;
foreach (string s in str_array)
{
    dict.Add(i++, s);
}
于 2012-07-17T13:10:31.923 回答
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()

于 2012-07-17T13:11:02.680 回答
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();

请注意,只有在遇到性能问题foreach时才需要担心 LinQ 性能。只有巨大的操作在性能上会有所不同。

另请参阅“嵌套 foreach”与“lambda/linq 查询”性能(LINQ-to-Objects)

于 2012-07-17T13:20:46.080 回答