C#:为什么字典比列表快得多?

时间:2013-06-07 06:33:04

标签: c# .net performance

我正在测试从Dictionary VS列表中获取数据的速度。
我用这段代码测试:

    internal class Program
{
    private static void Main(string[] args)
    {
        var stopwatch = new Stopwatch();
        List<Grade> grades = Grade.GetData().ToList();
        List<Student> students = Student.GetStudents().ToList();

        stopwatch.Start();
        foreach (Student student in students)
        {
            student.Grade = grades.Single(x => x.StudentId == student.Id).Value;
        }
        stopwatch.Stop();
        Console.WriteLine("Using list {0}", stopwatch.Elapsed);
        stopwatch.Reset();
        students = Student.GetStudents().ToList();
        stopwatch.Start();
        Dictionary<Guid, string> dic = Grade.GetData().ToDictionary(x => x.StudentId, x => x.Value);
        foreach (Student student in students)
        {
            student.Grade = dic[student.Id];
        }
        stopwatch.Stop();
        Console.WriteLine("Using dictionary {0}", stopwatch.Elapsed);
        Console.ReadKey();
    }
}

public class GuidHelper
{
    public static List<Guid> ListOfIds=new List<Guid>();

    static GuidHelper()
    {
        for (int i = 0; i < 10000; i++)
        {
            ListOfIds.Add(Guid.NewGuid());
        }
    }
}


public class Grade
{
    public Guid StudentId { get; set; }
    public string Value { get; set; }

    public static IEnumerable<Grade> GetData()
    {
        for (int i = 0; i < 10000; i++)
        {
            yield return new Grade
                             {
                                 StudentId = GuidHelper.ListOfIds[i], Value = "Value " + i
                             };
        }
    }
}

public class Student
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Grade { get; set; }

    public static IEnumerable<Student> GetStudents()
    {
        for (int i = 0; i < 10000; i++)
        {
            yield return new Student
                             {
                                 Id = GuidHelper.ListOfIds[i],
                                 Name = "Name " + i
                             };
        }
    }
}

有记忆中的学生和成绩列表,他们有共同的StudentId。
在第一种方式中,我尝试使用LINQ找到一个学生的成绩,在我的机器上花了将近7秒的时间,另一方面我首先将List转换为字典,然后使用需要不到一秒钟的密钥从字典中查找学生成绩。 enter image description here

8 个答案:

答案 0 :(得分:115)

执行此操作时:

student.Grade = grades.Single(x => x.StudentId == student.Id).Value;

如上所述,它必须枚举整个List,直到它在List中找到具有正确studentId的条目(条目0与lambda匹配?No ...条目1是否与lambda匹配? ..等等)。这是O(n)。既然你为每个学生做了一次,那就是O(n ^ 2)。

但是当你这样做时:

student.Grade = dic[student.Id];

如果你想在字典中按键找到某个元素,它可以立即跳转到字典中的位置 - 这是O(1)。 O(n)为每个学生做这件事。 (如果你想知道这是怎么做的 - Dictionary对键运行一个数学运算,它将它变成一个值,它是字典里面的一个位置,它与插入时的位置相同)

因此,字典更快,因为您使用了更好的算法。

答案 1 :(得分:11)

使用词典时,您使用来检索您的信息,这使得它能够更有效地找到它,使用列表您正在使用Single Linq表达式,因为它是一个列表,除了查看整个列表之外没有其他选项可用于想要的项目。

答案 2 :(得分:10)

原因是因为字典是查找,而列表是迭代。

Dictionary使用哈希查找,而您的列表需要遍历列表,直到每次从结果开始到结果为止。

换句话说。该列表将比第一个项目上的字典更快,因为没有任何东西可以查找。这是第一项,热潮......它已经完成了。但第二次列表必须查看第一项,然后是第二项。第三次通过它必须查看第一项,然后是第二项,然后是第三项......等等。

因此,每次迭代查找都会花费越来越多的时间。列表越大,所需的时间越长。虽然字典总是或多或少固定的查找时间(它也随着字典变大而增加,但速度要慢得多,所以通过比较它几乎是固定的)。

答案 3 :(得分:8)

Dictionary使用散列来搜索数据。字典中的每个项目都存储在包含相同哈希的项目桶中。它的速度要快得多。

尝试对列表进行排序,然后会更快一些。

答案 4 :(得分:6)

字典使用hash table,它是一个很好的数据结构,因为它几乎瞬间将输入映射到相应的输出,它具有已经指出的O(1)的复杂性,这意味着或多或少的立即检索。

它的缺点是,为了性能,你需要提前有很多空间(取决于实现,它是单独的链接或线性/二次探测,你可能至少需要你计划存储的数量,在后一种情况下可能会加倍)并且您需要一个良好的散列算法,将您的输入("John Smith")唯一映射到相应的输出,例如数组中的位置(hash_array[34521])。

同样按排序顺序列出条目是个问题。如果我引用维基百科:

  

按特定顺序列出所有n个条目通常需要a   单独的排序步骤,其成本与每个条目的log(n)成比例。

请阅读linear probingseparate chaining,了解一些gorier详情:)

答案 5 :(得分:3)

Dictionary基于哈希表,这是一种查找事物的相当有效的算法。在列表中,您必须逐个元素地去找东西。

这都是数据组织的问题......

答案 6 :(得分:2)

在查找数据时,键控集合总是比非键控集合更快。这是因为非键控集合必须枚举其元素以找到您要查找的内容。在键控集合中,您可以直接通过键访问元素。

这些是用于将列表与字典进行比较的一些不错的文章。

Here。这是one

答案 7 :(得分:-1)

从MSDN-字典中提到的词接近O(1),但我认为这取决于所涉及的类型。

Dictionary(TKey,TValue)泛型类提供了从一组键到一组值的映射。字典的每个加法项都包含一个值及其关联的键。通过使用键的值检索值非常快,接近O(1),因为Dictionary类是作为哈希表实现的。

注意: 检索速度取决于为TKey指定的类型的哈希算法的质量。

List(TValue)没有实现哈希查找,因此它是顺序的,性能为O(n)。它还取决于所涉及的类型,需要考虑装箱/拆箱。