web-dev-qa-db-ja.com

Lookup()とDictionary(Of list())の違い

どのデータ構造が最も効率的で、どのデータ構造をいつ/どこで使用するかを頭に入れようとしています。

さて、単に構造を十分に理解していないだけかもしれませんが、ILookup(of key, ...)Dictionary(of key, list(of ...))とどう違うのですか?

また、どこでILookupを使用したいのでしょうか。また、プログラムの速度/メモリ/データへのアクセスなどの点でより効率的ですか?

147
John Bustos

2つの重要な違い:

  • Lookupは不変です。 Yay :)(少なくとも、具体的なLookupクラスは不変であり、ILookupインターフェースは変化するメンバーを提供しないと信じています。(could他の可変実装、もちろん。)
  • ルックアップに存在しないキーをルックアップすると、KeyNotFoundExceptionの代わりに空のシーケンスが返されます。 (したがって、TryGetValue、AFAICRはありません。)

効率的には同等である可能性が高い-たとえば、ルックアップは舞台裏でDictionary<TKey, GroupingImplementation<TValue>>を使用する場合があります。要件に基づいて選択してください。個人的には、ほとんどの場合、上記の最初の2つの点が原因で、ルックアップはDictionary<TKey, List<TValue>>よりも適しています。

実装の詳細として、値に使用されるIGrouping<,>の具体的な実装はIList<TValue>を実装することに注意してください。つまり、Count()ElementAt()などで使用すると効率的です。

229
Jon Skeet

興味深いのは、実際の最大の違いについて誰も言及していないことです( MSDN から直接取得):

ルックアップは辞書に似ています。違いは、ディクショナリはキーを単一の値にマップするのに対して、ルックアップはキーを値のコレクションにマップすることです。

35

Dictionary<Key, List<Value>>Lookup<Key, Value>の両方が、同様の方法で編成されたデータを論理的に保持でき、どちらも同じ効率の順序です。主な違いはLookupは不変です:Add()メソッドもパブリックコンストラクターもありません(Jonが述べたように、例外なしで存在しないキーを照会し、そのキーをグルーピング)。

どちらを使用するかは、実際にどのように使用するかによって異なります。常に変更されている複数の値へのキーのマップを維持している場合、Dictionary<Key, List<Value>>は可変であるため、おそらく優れています。

ただし、データのシーケンスがあり、キーで整理されたデータの読み取り専用ビューが必要な場合は、ルックアップを構築するのが非常に簡単で、読み取り専用のスナップショットが得られます。

32

ILookup<K,V>Dictionary<K, List<V>>の主な違いは、辞書が変更可能であることです。キーを追加または削除したり、検索されたリストからアイテムを追加または削除したりできます。 ILookup不変であり、作成後は変更できません。

両方のメカニズムの基礎となる実装は同じか類似しているため、検索速度とメモリフットプリントはほぼ同じになります。

11
Servy

まだ言及されていない別の違いは、Lookup() nullキーをサポート

LookupクラスはILookupインターフェイスを実装します。ルックアップは、複数の値が同じキーにマップできることと、ヌルキーがサポートされていることを除いて、ディクショナリに非常に似ています。

9

例外がオプションではない場合、ルックアップに進みます

Dictionaryと同じくらい効率的な構造を取得しようとしているが、入力に重複キーがないことを確実に知らない場合、Lookupの方が安全です。

別の回答で述べたように、nullキーもサポートし、任意のデータを照会すると常に有効な結果を返すため、未知の入力に対してより回復力があるように見えます(例外が発生するのは辞書よりも少なくなります)。

そして、それをSystem.Linq.Enumerable.ToDictionary関数と比較すると特に当てはまります。

// won't throw
new[] { 1, 1 }.ToLookup(x => x); 

// System.ArgumentException: An item with the same key has already been added.
new[] { 1, 1 }.ToDictionary(x => x);

別の方法として、foreachループ内に独自の重複キー管理コードを記述する方法があります。

パフォーマンスの考慮事項、辞書:明確な勝者

リストが不要で、膨大な数のアイテムを管理する場合は、Dictionary(または独自のカスタム調整構造)の方が効率的です。

        Stopwatch stopwatch = new Stopwatch();
        var list = new List<string>();
        for (int i = 0; i < 5000000; ++i)
        {
            list.Add(i.ToString());
        }
        stopwatch.Start();
        var lookup = list.ToLookup(x => x);
        stopwatch.Stop();
        Console.WriteLine("Creation: " + stopwatch.Elapsed);

        // ... Same but for ToDictionary
        var lookup = list.ToDictionary(x => x);
        // ...

Lookupは各キーのアイテムのリストを保持する必要があるため、辞書よりも遅くなります(膨大な数のアイテムの場合は約3倍遅くなります)

ルックアップ速度:作成:00:00:01.5760444

辞書速度:作成:00:00:00.4418833

4
Fab