web-dev-qa-db-ja.com

C#:辞書がリストよりもずっと速いのはなぜですか?

辞書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を辞書に変換し、 1秒未満。 enter image description here

61
Unforgiven

これを行うとき:

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

書かれているように、正しいstudentIdを持つリストのエントリを見つけるまで、List全体を列挙する必要があります(エントリ0はラムダと一致しますか?いいえ...エントリ1はラムダと一致しますか?いいえ...など)。これはO(n)です。すべての生徒に対して1回行うので、O(n ^ 2)です。

ただし、これを行う場合:

student.Grade = dic[student.Id];

ディクショナリ内のキーで特定の要素を検索する場合、ディクショナリ内のある場所に即座にジャンプできます-これはO(1)です。 O(n)すべての学生に対してそれを行うため。(これがどのように行われるかを知りたい場合-辞書はキーに対して数学的操作を実行し、それが場所である値に変わります辞書内。挿入されたときに配置した場所と同じです)

したがって、より良いアルゴリズムを使用したため、辞書は高速になります。

116
Patashu

辞書を使用する場合、keyを使用して情報を取得します。これにより、リストでSingle Linq式を使用します。これはリストであるため、 リスト全体を見る以外のオプションはありません。

11
Ron.B.I

その理由は、辞書はルックアップであり、リストは反復であるためです。

辞書はハッシュルックアップを使用しますが、リストは毎回、最初から結果まで結果が見つかるまでリスト内を移動する必要があります。

別の言い方をすれば。検索するものがないため、リストは最初の項目の辞書よりも高速になります。それは最初のアイテム、ブームです。しかし、2回目はリストが最初のアイテムを調べ、次に2番目のアイテムを調べなければなりません。 3回目は、最初の項目、次に2番目の項目、3番目の項目などを調べる必要があります。

そのため、検索を繰り返すたびに時間がかかります。リストが大きいほど、時間がかかります。ディクショナリは常にほぼ一定のルックアップ時間ですが(ディクショナリが大きくなると増加しますが、ペースははるかに遅いため、比較するとほぼ固定されています)。

10

辞書はハッシュを使用してデータを検索します。辞書内の各アイテムは、同じハッシュを含むアイテムのバケットに保存されます。それはずっと速いです。

リストを並べ替えてみてください、それは少し速くなります。

8

辞書は ハッシュテーブル を使用します。入力をほぼ瞬時に対応する出力にマッピングするため、優れたデータ構造です。O(1)既に指摘したように、これは多かれ少なかれ即時の検索を意​​味します。

その短所は、パフォーマンスのために事前に多くのスペースが必要であるということです(実装に応じて、チェーンまたはリニア/クアドラティブプロービングを別々に実行するかどうかに応じて、少なくとも保存するのと同じくらい、おそらく2倍の後者の場合)、入力を一意にマッピングする適切なハッシュアルゴリズムが必要です("John Smith")配列内の位置などの対応する出力(hash_array[34521])。

また、エントリをソートされた順序でリストすることは問題です。ウィキペディアを引用する場合:

特定の順序でn個すべてのエントリをリストするには、通常、エントリごとのlog(n)に比例するコストの個別の並べ替え手順が必要です。

リニアプローブ および 個別のチェーン の詳細を参照してください:)

6
Nobilis

辞書は、物事を検索するためのかなり効率的なアルゴリズムであるハッシュテーブルに基づいています。リストでは、何かを見つけるために要素ごとに移動する必要があります。

それはすべてデータ編成の問題です...

3
JeffRSon

データの検索に関しては、キー付きコレクションはキーなしコレクションよりも常に高速です。これは、キーのないコレクションが、探しているものを見つけるために要素を列挙する必要があるためです。キー付きコレクションでは、キーを介して要素に直接アクセスできます。

これらは、リストと辞書を比較するための素晴らしい記事です。

ここ 。そして、これは one です。

2
aiapatag