LINQ ToDictionary方法与复制字典迭代循环之间的性能差异

时间:2018-07-29 15:37:35

标签: c# performance linq

在尝试查看使用ToDictionary复制字典与使用迭代循环复制字典之间的区别时,我看到了非常大的性能差异。
在下面的代码中,我创建了Dictionary<int, int>并使用LINQ和Non LINQ方式将其复制。

代码:

public static void Main()
{
    const int originalDictionarySize = 10000;

    //////Creating Dictionary////////////// 
    var originalDictionary = new Dictionary<int, int>();
    for (var i = 0; i < originalDictionarySize; i++)
    {
        originalDictionary.Add(i, i);
    }
    //////Copy with Iterative Loop////////////// 
    IteraqtiveLoop(originalDictionary);

    //////Copy with LINQ///////////////////////// 
    CopyWIthLinq(originalDictionary);

    Console.ReadLine();
}

private static void IteraqtiveLoop(Dictionary<int, int> 
     originalDictionary)
{
    var sw = Stopwatch.StartNew();
    var dictionary2 = new Dictionary<int, int>(originalDictionary.Count);
    foreach (var kvp in originalDictionary)
    {
        dictionary2.Add(kvp.Key, kvp.Value);
    }
    sw.Stop();
    var endTime = sw.Elapsed;
    Console.WriteLine("The running time of copy with iterative loop: " + 
endTime);
}

private static void CopyWIthLinq(Dictionary<int, int> originalDictionary)
{
    var sw = Stopwatch.StartNew();
    var dictionary3 = originalDictionary.ToDictionary(i => i, i => i);
    sw.Stop();
    var endTime2 = sw.Elapsed;
    Console.WriteLine("The running time of copy with LINQ: " + endTime2);
}

输出:

The running time of copy with iterative loop: 00:00:00.0005765                                                           
The running time of copy with LINQ: 00:00:02.5989753 

为什么差异如此之大? 我使用其他类型进行了此实验:

Dictionary<int, float>Dictionary<int, MyObject>-MyObject有2个成员,分别是stringint

在其他实验中,Linq和Non-Linq之间存在差异,但是只有Dictionary<int, int>时差如此之大。

1 个答案:

答案 0 :(得分:7)

区别之一是“迭代”方法将容量传递给Dictionary构造函数,从而避免了重新哈希。尽管LINQ实现可以执行相同的优化(目前,整个框架实现无法实现)。

但是产生巨大性能差异的主要区别是您的LINQ实现

var dictionary3 = originalDictionary.ToDictionary(i => i, i => i);

不产生Dictionary<int, int>,但产生Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>

这是因为键选择器和元素选择器中的i的类型都是KeyValuePair<int, int>,因为Dictionary<int, int>IEnumerable<KeyValuePair<int, int>>。并且GetHashCode / Equals方法主导操作(Dictionary.Add)的方法对于KeyValuePair结构要比简单int慢得多(不计算结构堆栈复制开销调用几种方法时)。

应该是这样:

var dictionary3 = originalDictionary.ToDictionary(e => e.Key, e => e.Value);

它仍然会慢一些,但不会很大。

更新:正如@ 2kay在评论中正确提及的那样,GetHashCode的{​​{1}}在KeyValuePair<int, int>和{{1 }}与您的测试相同,这对于像Key这样的哈希结构来说是最坏的情况,它使Value(检查重复项)的操作Dictionary(二次)的时间复杂度和解释了此特定测试在性能上的巨大差异。