SortedList <>、SortedDictionary <>およびDictionary <>


98

それを見つけて、同じインターフェースSortedList<TKey, TValue> SortedDictionary<TKey, TValue>Dictionary<TKey, TValue>実装します。

  1. ときに我々はのために選ぶ必要がありますSortedListし、SortedDictionaryオーバーDictionary
  2. アプリケーションSortedListとの違いは何SortedDictionaryですか?

回答:


101
  1. 2つの要素のいずれかで要素を反復すると、要素がソートされます。とは違いDictionary<T,V>ます。

  2. MSDNは違いアドレスSortedList<T,V>とをSortedDictionary<T,V>

SortedDictionary(TKey、TValue)ジェネリッククラスは、O(log n)を取得するバイナリ検索ツリーです。ここで、nはディクショナリ内の要素の数です。この点で、それはSortedList(TKey、TValue)ジェネリッククラスに似ています。2つのクラスには同様のオブジェクトモデルがあり、両方にO(log n)取得があります。2つのクラスが異なるのは、メモリの使用と挿入および削除の速度です。

SortedList(TKey、TValue)は、SortedDictionary(TKey、TValue)よりも少ないメモリを使用します。

SortedDictionary(TKey、TValue)は、ソートされていないデータの挿入および削除操作が高速です:SortedList(TKey、TValue)のO(n)ではなく、O(log n)。

リストが並べ替えられたデータから一度に入力される場合、SortedList(TKey、TValue)はSortedDictionary(TKey、TValue)より高速です。


21
もう1つの実用的な違いは、SortedList(キーによる検索でSortedDictionaryはなく)インデックスで検索できることと、できないことです。
Andrew Savinykh、2015

65

ここに画像の説明を入力してください

辞書の違いについて触れておきます。

上の図はDictionary<K,V>、すべての場合でSortedアナログよりも高速か高速であることを示していますが、要素の順序が必要な場合、たとえばそれらを印刷する場合Sortedは、1つが選択されます。

Src:http : //people.cs.aau.dk/~normark/oop-csharp/html/notes/collections-note-time-complexity-dictionaries.html


1
素晴らしい概観。元の質問にはありませんがImmutable、これらのディクショナリのバージョン間で選択した場合、Sortedバージョンは実際にはソートされていないものよりも40〜50%高速であることが多いことに注意してください(まだO(log(n))ですが、操作ごとに著しく高速です)。 。ただし、入力がどのようにソートされるかによって、タイミングが異なる場合があります。stackoverflow.com/a/30638592/111575を
Abel

21

パフォーマンステストの結果をまとめると、SortedList対SortedDictionary対Dictionary対Hashtableであり、さまざまなシナリオでの最高から最低までの結果です。

メモリ使用量:

SortedList<T,T>
Hashtable
SortedDictionary<T,T>
Dictionary<T,T>

挿入:

Dictionary<T,T>
Hashtable
SortedDictionary<T,T>
SortedList<T,T>

検索操作:

Hashtable
Dictionary<T,T>
SortedList<T,T>
SortedDictionary<T,T>

foreachループ操作

SortedList<T,T>
Dictionary<T,T>
Hashtable
SortedDictionary<T,T>

1
これらのテスト結果を調べるとき、SortedDictionaryの存在理由を疑問視できます。
ビーウルフ2018

1
あなたの場合はCollectionニーズがなければsorted、あなたは忘れることができHashtableそしてDictionary:あなたはワンショットであなたのコレクションを取り込む場合- > SortedListのために行くが、あなたが予想される場合には、多くの場合に必要になります.Addし、.Removeアイテム- > SortedDictionaryのために行きます。
あま

おそらく、そのsorted意味を明確にする必要がありFor Each MyItem in Collectionます。最初.Addにアイテムを編集した順序で処理されるのではなく、a sorted CollectionKey値のクリテロン(で定義されているIComparer)に従って順序で処理します。たとえば、キーが文字列の場合、コレクションはデフォルトでキーのアルファベット順で処理されますが、カスタムの並べ替えルールをいつでも定義できます。
あま

9
  1. コレクションを繰り返し処理するときに、コレクションをキーでソートする場合。データを並べ替える必要がない場合は、ディクショナリだけを使用したほうがよいでしょう。パフォーマンスが向上します。

  2. SortedListとSortedDictionaryはほとんど同じことを行いますが、実装方法が異なるため、ここで説明する長所と短所は異なります


8

提案された回答はパフォーマンスに焦点を当てていることがわかります。以下の記事では、パフォーマンスに関する新しい情報は提供されていませんが、根本的なメカニズムについて説明しています。またCollection、質問で言及されている3つのタイプに焦点を合わせていないが、System.Collections.Generic名前空間のすべてのタイプに対応していることに注意してください。

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/06/16/c.net-fundamentals-choosing-the-right-collection-class.aspx

抽出物:

辞書<>

辞書はおそらく最もよく使われる連想コンテナクラスです。辞書は隠されたハッシュテーブルを使用するため、連想検索/挿入/削除の最高速クラスです。キーはハッシュされるため、キータイプはGetHashCode()およびEquals()を適切に実装するか、構築時にディクショナリに外部IEqualityComparerを提供する必要があります。ディクショナリ内のアイテムの挿入/削除/ルックアップ時間は、一定の時間-O(1)-で償却されます。つまり、ディクショナリがどれほど大きくても、何かを見つけるのにかかる時間は比較的一定です。これは、高速ルックアップに非常に適しています。唯一の欠点は、ハッシュテーブルを使用するという性質上、辞書が順序付けされていないことです。辞書の項目を順番に簡単にたどることはできません

SortedDictionary <>

SortedDictionaryは、使用法はディクショナリに似ていますが、実装が大きく異なります。SortedDictionaryは、キー順に項目を維持するために、カバーの下にバイナリツリーを使用しています。並べ替えの結果、キーに使用される型は、キーが正しく並べ替えられるように、IComparableを正しく実装する必要があります。ソートされたディクショナリは、アイテムを順番に維持する機能と少しのルックアップ時間を交換します。したがって、ソートされたディクショナリの挿入/削除/ルックアップ時間は対数-O(log n)です。一般的に言えば、対数時間を使用すると、コレクションのサイズを2倍にすることができ、アイテムを見つけるために追加の比較を1回実行するだけで済みます。高速なルックアップが必要であるが、キーによる順序でコレクションを維持できるようにしたい場合は、SortedDictionaryを使用します。

SortedList <>

SortedListは、汎用コンテナ内の他のソートされた連想コンテナクラスです。ここでも、SortedDictionaryと同様に、SortedList はキーを使用してキーと値のペアをソートします。ただし、SortedDictionaryとは異なり、SortedList内のアイテムは、アイテムの並べ替えられた配列として格納されます。。これは、挿入と削除が線形であることを意味します-O(n)-アイテムを削除または追加すると、リスト内のすべてのアイテムが上下にシフトする場合があるためです。ただし、SortedListはバイナリ検索を使用して、キーでリスト内の任意の項目を見つけることができるため、ルックアップ時間はO(log n)です。では、なぜこれをしたいのでしょうか?答えは、SortedListを事前にロードする場合、挿入は遅くなりますが、配列のインデックス付けはオブジェクトリンクをたどるよりも高速であるため、検索はSortedDictionaryよりもわずかに高速です。繰り返しますが、高速なルックアップが必要で、コレクションをキー順に管理したい場合や、挿入や削除がほとんど行われない場合にこれを使用します。


基本的な手順の暫定的な要約

私はすべてが正しくできていないと確信しているので、フィードバックは大歓迎です。

  • すべての配列はサイズnです。
  • ソートされていない配列= .Add / .RemoveはO(1)ですが、.Item(i)はO(n)です。
  • 並べ替えられた配列= .Add / .RemoveはO(n)ですが、.Item(i)はO(log n)です。

辞書

記憶

KeyArray(n) -> non-sorted array<pointer>
ItemArray(n) -> non-sorted array<pointer>
HashArray(n) -> sorted array<hashvalue>

追加

  1. #O HashArray(n) = Key.GetHash(1)を追加
  2. #O KeyArray(n) = PointerToKey(1)を追加
  3. #O ItemArray(n) = PointerToItem(1)を追加

削除する

  1. For i = 0 to niどこを見つけるHashArray(i) = Key.GetHash #O(log n)(ソートされた配列)
  2. #O HashArray(i)(n)(ソートされた配列)を削除
  3. #O KeyArray(i)(1)を削除
  4. #O ItemArray(i)(1)を削除

アイテムを入手する

  1. For i = 0 to niどこを見つけるHashArray(i) = Key.GetHash#O(log n)(ソートされた配列)
  2. 戻る ItemArray(i)

ループスルー

  1. For i = 0 to n、戻る ItemArray(i)

SortedDictionary

記憶

KeyArray(n) = non-sorted array<pointer>
ItemArray(n) = non-sorted array<pointer>
OrderArray(n) = sorted array<pointer>

追加

  1. #O KeyArray(n) = PointerToKey(1)を追加
  2. #O ItemArray(n) = PointerToItem(1)を追加
  3. For i = 0 to niどこを見つけるKeyArray(i-1) < Key < KeyArray(i)(を使用してICompare)#O(n)
  4. #O OrderArray(i) = n(n)(ソートされた配列)を追加

削除する

  1. For i = 0 to niどこでKeyArray(i).GetHash = Key.GetHash#O(n)を見つける
  2. #O KeyArray(SortArray(i))(n)を削除
  3. #O ItemArray(SortArray(i))(n)を削除
  4. #O OrderArray(i)(n)(ソートされた配列)を削除

アイテムを入手する

  1. For i = 0 to niどこでKeyArray(i).GetHash = Key.GetHash#O(n)を見つける
  2. 戻る ItemArray(i)

ループスルー

  1. For i = 0 to n、戻る ItemArray(OrderArray(i))

SortedList

記憶

KeyArray(n) = sorted array<pointer>
ItemArray(n) = sorted array<pointer>

追加

  1. For i = 0 to ni場所を見つけるKeyArray(i-1) < Key < KeyArray(i)(を使用してICompare)#O(log n)
  2. #O KeyArray(i) = PointerToKey(n)を追加
  3. #O ItemArray(i) = PointerToItem(n)を追加

削除する

  1. For i = 0 to niどこを見つけるKeyArray(i).GetHash = Key.GetHash#O(log n)
  2. #O KeyArray(i)(n)を削除
  3. #O ItemArray(i)(n)を削除

アイテムを入手する

  1. For i = 0 to niどこを見つけるKeyArray(i).GetHash = Key.GetHash#O(log n)
  2. 戻る ItemArray(i)

ループスルー

  1. For i = 0 to n、戻る ItemArray(i)

0

@Levによって提示された各ケースにパフォーマンススコアを割り当てようとして、次の値を使用しました。

  • O(1)= 3
  • O(log n)= 2
  • O(n)= 1
  • O(1)またはO(n)= 2
  • O(log n)またはO(n)= 1.5

結果は(高い=より良い)です。

Dictionary:       12.0 
SortedDictionary:  9.0 
SortedList:        6.5

もちろん、すべてのユースケースは特定の操作により大きな重みを与えます。


1
目安として、O(n)の重みはnであるのに対し、O(log n)の重みはlog(n)/ log(2)(nが2倍になるたびに+1)になります。したがって、重み付けは最大4のサイズで正しくなります。それを超えると、2:1の比率がすぐに上がります。たとえば、n = 100の場合、O(log n)= 15になります。同様の考え方に従って、O(1)の重みは100になります。結論:O(n)はかなり迅速に戦闘を失います。そうでない場合は、アレイが小さいことを意味し、効率は問題になりません。
あま
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.