web-dev-qa-db-ja.com

SortedDictionary <TKey、TValue>でSortedList <TKey、TValue>を使用する場合

これは question の複製のように見える場合があり、「 SortedListSortedDictionary ?の違いは何ですか?」残念ながら、回答はMSDNのドキュメントを引用するだけであり(パフォーマンスとメモリ使用の違いがあることを明確に述べています)、実際には質問に答えていません。

実際、MSDNによると、この質問は同じ回答を得られません。

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

  • SortedList<TKey, TValue>は、SortedDictionary<TKey, TValue>よりも少ないメモリを使用します。

  • SortedDictionary<TKey, TValue>には、O(n) for SortedList<TKey, TValue>)とは対照的に、ソートされていないデータO(log n)の挿入および削除操作が高速化されています。

  • ソートされたデータからリストが一度に作成される場合、SortedList<TKey, TValue>SortedDictionary<TKey, TValue>よりも高速です。

したがって、明らかに、これはSortedList<TKey, TValue>がより適切な選択肢であることを示しますnlessnsortedデータの挿入および削除操作を高速化する必要があります。

上記の情報がSortedDictionary<TKey, TValue>を使用する実際的な理由(現実世界、ビジネスケースなど)を考えると、疑問はまだ残っています。パフォーマンス情報に基づいて、SortedDictionary<TKey, TValue>を持つ必要はまったくないことを意味します。

63
Scott Dorman

MSDNドキュメントがSortedListおよびSortedDictionaryにどれほど正確かはわかりません。両方ともバイナリ検索ツリーを使用して実装されていると言っているようです。しかし、SortedListがバイナリ検索ツリーを使用する場合、追加時にSortedDictionaryよりもはるかに遅いのはなぜですか?

とにかく、ここにいくつかのパフォーマンステスト結果があります。

各テストは、10,000個のint32キーを含むSortedList/SortedDictionaryで動作します。各テストは1,000回繰り返されます(リリースビルド、デバッグなしで開始)。

テストの最初のグループは、0〜9,999のキーを順番に追加します。 2番目のグループのテストでは、ランダムにシャッフルされたキーが0〜9,999の間で追加されます(すべての番号が1回だけ追加されます)。

***** Tests.PerformanceTests.SortedTest

SortedDictionary Add sorted: 4411 ms
SortedDictionary Get sorted: 2374 ms


SortedList Add sorted: 1422 ms
SortedList Get sorted: 1843 ms

***** Tests.PerformanceTests.UnsortedTest

SortedDictionary Add unsorted: 4640 ms
SortedDictionary Get unsorted: 2903 ms


SortedList Add unsorted: 36559 ms
SortedList Get unsorted: 2243 ms

すべてのプロファイリングと同様に、重要なことは相対的なパフォーマンスであり、実際の数値ではありません。

ご覧のとおり、ソートされたデータでは、ソートされたリストはSortedDictionaryよりも高速です。ソートされていないデータの場合、SortedListは取得時にわずかに高速ですが、追加時に約9倍遅くなります。

両方が内部でバイナリツリーを使用している場合、SortedListの場合、ソートされていないデータの追加操作が非常に遅いことは非常に驚くべきことです。並べ替えられたリストが、並べ替えられた線形データ構造に同時にアイテムを追加している可能性もあり、これにより速度が低下します。

ただし、SortedListのメモリ使用量は、SortedDictionary以上であるか、少なくとも等しいと予想されます。しかし、これはMSDNのドキュメントに記載されている内容と矛盾しています。

53
Ash

MSDNがSortedList<TKey, TValue>がその実装にバイナリツリーを使用すると言っている理由がわかりません。なぜなら、Reflectorのような逆コンパイラでコードを見ると、その真実ではないことに気づくからです。

SortedList<TKey, TValue>は、時間とともに成長する単なる配列です。

要素を挿入するたびに、まず配列に十分な容量があるかどうかをチェックします。十分でない場合は、より大きな配列が再作成され、古い要素がコピーされます(List<T>など)

その後、バイナリ検索を使用して、要素を挿入するためにwhereを検索します(これは、配列がインデックス付け可能で、既にソートされているため可能です)。

配列のソートを維持するために、1つの位置で挿入される要素の位置の後にあるすべての要素を移動(またはプッシュ)Array.Copy()を使用)。

例:

// we want to insert "3" 

2  
4  <= 3
5
8
9
.      
.      
.  

// we have to move some elements first

2
.  <= 3
4 
5  |
8  v
9
.
.

ソートされていない要素を挿入すると、SortedListのパフォーマンスが非常に悪い理由を説明します。ほとんどすべての挿入でいくつかの要素を再コピーする必要があります。実行する必要のない唯一のケースは、要素を配列の最後に挿入する必要がある場合です。

SortedDictionary<TKey, TValue>は異なり、バイナリツリーを使用して要素を挿入および取得します。また、ツリーのバランスを再調整する必要がある場合があるため、挿入時にコストがかかります(ただし、すべての挿入ではありません)。

SortedListまたはSortedDictionaryを使用して要素を検索するときのパフォーマンスは、バイナリ検索を使用するため非常に似ています。


私の意見では、neverSortedListを使用して配列をソートするだけです。要素が非常に少ない場合を除き、値をリスト(または配列)に挿入してからSort()メソッドを呼び出す方が常に高速です。

SortedListは、既にソートされた値のリスト(例:データベースから)があり、ソートされたままにして、ソートされていることを利用する操作を実行する場合(例:Contains()SortedListのメソッドは、線形検索の代わりにバイナリ検索を実行します)

SortedDictionarySortedListと同じ利点を提供しますが、挿入する値がまだソートされていない場合はパフォーマンスが向上します。


編集:.NET Framework 4.5を使用している場合、SortedDictionary<TKey, TValue>の代替はSortedSet<T>です。バイナリツリーを使用してSortedDictionaryと同じように機能しますが、キーと値は同じです。

36
tigrou

それらは2つの異なる目的のためのものですか?

.NETのこれら2つのコレクションタイプのセマンティック上の違いはあまりありません。どちらもキー付きルックアップを提供し、キーのソート順でエントリを保持します。ほとんどの場合、どちらでも大丈夫です。おそらく唯一の差別化要因は、インデックス付きの取得SortedListパーミットでしょう。

しかし、パフォーマンスは?

ただし、パフォーマンスの違いがあるため、mightを選択することを強くお勧めします。ここに、それらの漸近的な複雑さの表形式のビューがあります。

+------------------+---------+----------+--------+----------+----------+---------+
| Collection       | Indexed | Keyed    | Value  | Addition |  Removal | Memory  |
|                  | lookup  | lookup   | lookup |          |          |         |
+------------------+---------+----------+--------+----------+----------+---------+
| SortedList       | O(1)    | O(log n) | O(n)   | O(n)*    | O(n)     | Lesser  |
| SortedDictionary | n/a     | O(log n) | O(n)   | O(log n) | O(log n) | Greater |
+------------------+---------+----------+--------+----------+----------+---------+

* Insertion is O(1) for data that are already in sort order, so that each 
  element is added to the end of the list (assuming no resize is required).

概要

大まかにまとめると、次の場合にSortedList<K, V>が必要です。

  1. インデックス付きルックアップが必要です。
  2. メモリのオーバーヘッドを小さくすることが望ましいです。
  3. 入力データはすでに並べ替えられています(たとえば、dbから既に並べ替えられている場合)。

代わりに、次の場合にSortedDictionary<K, V>を優先します。

  1. 相対的な全体パフォーマンスが重要です(スケーリングに関して)。
  2. 入力データは順不同です。

コードの記述

SortedList<K, V>SortedDictionary<K, V>の両方がIDictionary<K, V>を実装するため、コードでIDictionary<K, V>を返すか、変数をIDictionary<K, V>として宣言できます。基本的に、実装の詳細とインターフェイスに対するコードを隠します。

IDictionary<K, V> x = new SortedDictionary<K, V>(); //for eg. 

将来的には、あるコレクションのパフォーマンス特性に満足できない場合に、どちらからでも簡単に切り替えることができます。


2つのコレクションタイプの詳細については、元の question リンクを参照してください。

10
nawfal

パフォーマンスの違いを視覚的に表現。

enter image description here

3
Lev

これですべてです。キーの取得は同等ですが、辞書を使用すると追加がはるかに高速になります。

キーと値のコレクションを反復処理できるため、できるだけSortedListを使用するようにします。私の知る限り、これはSortedDictionaryでは不可能です。

これについてはわかりませんが、私が知っている限りでは、辞書はツリー構造にデータを格納しますが、リストはデータを線形配列に格納します。これにより、辞書の挿入と削除が高速になります。これは、移動するメモリが少ないためです。また、SortedDictionaryではなくSortedListsを反復処理できる理由についても説明します。

2
David Rutten

私たちにとって重要な考慮事項は、しばしば小さな辞書(<100要素)があり、現在のプロセッサはシーケンシャルメモリへのアクセスがはるかに速く、予測が困難な分岐をほとんど実行しないという事実です。 (つまり、ツリーをトラバースするのではなく線形配列を反復処理する)したがって、辞書に含まれる要素が約60未満の場合、多くの場合、SortedList <>は最も高速でメモリ効率の高い辞書になります。

0
user3290232