- ジェネリックコレクション ページ一覧
SortedListとSortedDictionary
System.Collections.Generic.SortedListクラスとSystem.Collections.Generic.SortedDictionaryクラスは非常によく似たコレクションクラスです。 どちらもDictionaryクラスと同じようにキーと値の対(ペア)で要素を格納します。 キーを指定することにより、そのキーに対応する要素の値を取得することができます。
SortedListクラス・SortedDictionaryクラスでは、要素が設定(追加)される時点でキーに基づいて自動的に要素の並べ替え(ソート)が行われます。 これはDictionaryには無い機能です。 Sortedの名前が示す通り、格納される要素は常にソートされた状態になります。
これらの特徴はSortedListとSortedDictionaryに共通するものですが、SortedListとSortedDictionaryの相違点の1つにインデックスを指定したアクセスができるかどうかがあります。
SortedListは名前が示すとおりListと似た特徴を持ち、インデックスを指定してキーや値を取得したり、逆にキーや値からインデックスを検索したりすることができます。 一方、SortedDictionaryはDictionaryと同様にインデックスを指定したアクセスはできません。 このような特徴と関連して、要素の挿入・削除操作の速度やメモリ使用量などのパフォーマンス面にも違いが現れます。
SortedListに格納される要素に割り振られるインデックスは、あくまで各要素を並べ替えた時の順序となります。 Listのように要素を追加した順にインデックスが割り振られるわけではありません。 キー・値のペアで要素を格納し、かつ要素を追加した順にインデックスを割り振りたい場合はOrderedDictionaryクラスを使います。
基本操作
インスタンスの作成・要素の取得・設定・列挙
インスタンスの作成、要素の取得と設定、要素の列挙などの操作はDictionaryクラスを使う場合と変わりありません。 foreach文で列挙を行う際にソートされた状態で列挙される点のみが異なります。
Keysプロパティを列挙した場合も同様に、キーがソートされた状態で列挙されます。 Valuesプロパティを列挙した場合は、ソートされたキーと同じ順序でキーに対応する値が列挙されます(値単独でソートされた順序にはなりません)。
コレクション初期化子
Dictionaryの場合と同様、SortedList・SortedDictionaryでもコレクション初期化子を使ってインスタンスの作成と要素の追加を同時に行うことができます。 コレクション初期化子はC# 3.0(Visual C# 2008)以降、VB 10(Visual Basic 2010)以降でサポートされます。
存在しないキーを参照した場合・既にキーが存在する場合
存在しないキーを参照した場合の動作はDictionaryと同様で、例外KeyNotFoundExceptionがスローされます。
キーを指定した上書き・追加の動作もDictionaryと同様です。 インデクサで既に存在するキーを指定した場合は値が上書きされ、Addメソッドで既に存在するキーを指定した場合は例外ArgumentExceptionがスローされます。
要素の検索・削除・存在チェック
Dictionaryクラスと同様、ContainsKeyメソッドやTryGetValueメソッドなどを使うことができます。
SortedListのみでサポートされる操作
SortedListとSortedDictionaryの相違点の一つとして、SortedListではインデックスを使った操作ができるという点が挙げられます。 以下の操作はSortedListのみで提供されるもので、SortedDictionaryでは提供されないものです。
インデックスを指定した操作
SortedListクラスでは、インデックスを指定してキー・値へアクセスすることができます。 KeysプロパティおよびValuesプロパティでは、インデックスを指定することで個々の要素のキーと値を取得できます。
なお、Keysプロパティ・Valuesプロパティを使ってインデックスを指定した値・キーの設定は行うことはできません。
SortedListでは、ソートされた順に従って各要素にインデックスが割り振られます(要素が追加された順ではありません)。 インデックス0の要素は、並べ替えた際に最初に位置する要素となります。 要素の追加や削除が行われる度に並べ替えられるため、要素に割り振られていたインデックスが変わることもあります。
キーと値のペアを格納するコレクションで、かつ要素が追加された順でインデックスが割り振られるようなコレクションが必要な場合は、OrderedDictionaryクラスを使うことができます。 ただし、このコレクションは非ジェネリックコレクションです。 ジェネリック版OrderedDictionaryではジェネリック版のOrderedDictionaryを実装したものを掲載しています。
キーまたは値からインデックスを取得する
キーや値からインデックスを取得したい場合は、IndexOfKeyメソッドおよびIndexOfValueメソッドを使うことでキー・値のインデックスを取得することができます。 SortedListに該当するキー・値が存在しない場合は-1が返されます。
インデックスを指定して要素を削除する
SortedListでは、Removeメソッドによるキーを指定した要素の削除の他に、RemoveAtメソッドによるインデックスを指定した要素の削除を行うことができます。
キー比較・ソート順のカスタマイズ
SortedList・SortedDictionaryにはSortメソッドやReverseメソッドなどコレクションに格納されている要素の順序を並べ替えるメソッドは用意されていません。 そのため、一度インスタンスを作成すると後から並べ替え順序を変更することはできません。
SortedList・SortedDictionaryでは、コンストラクタで適切なIComparer<T>インターフェイスを指定することでキーの比較や並べ替え順序を独自に定義したものに変更することができます。
以下の例で使用するIComparer<T>インターフェイスなど、比較処理に関する詳細については以下のページも合わせてご覧ください。
独自の並べ替え順序を定義する
次の例は、並べ替えの順序を独自に定義してSortedList・SortedDictionaryでの並べ替え順をカスタマイズするものです。 大文字小文字の違いを無視し、アルファベット順とは逆順(Z→Aの順)になるようにソートするIComparer<string>を実装したクラスCaseInsensitiveReverseStringComparerを作成し、それをSortedList・SortedDictionaryのコンストラクタに指定することで独自に定義した並べ替え順序となるようにしています。
実行結果からもわかるように、大文字小文字の違いがあっても同一のキーとして扱われるようになるため、値は上書きされるようになります。
降順・逆順にする
SortedList・SortedDictionaryにはReverseメソッドが用意されていないため、コレクションの内容を昇順から降順に並べ替え順序を変えることはできません。 並べ替え順序を変えるには新たに別のインスタンスを作成する必要があります。
次の例では、既存のSortedListのコピーを作成し、内容は同じで並べ替え順序だけを逆にしたインスタンスを作成することにより、逆順にしたSortedListを得ています。 新しいSortedListのインスタンスを作成する際、コンストラクタに既存のSortedListを指定することで同一の内容を持つインスタンスを作成します。 同時に、元のIComparer<T>とは逆の順序に並べ替えるようにしたIComparer<T>をコンストラクタに指定することで逆順に並べ替えるようにしています。
この例で紹介する方法では、ComparerプロパティからIComparer<T>を取得することにより、個別に並べ替え順序を定義することなく単に並べ替え順序を逆にしています。 そのため、キーの型に関わらずどのようなSortedListでも逆順にすることができます。
この方法は、SortedDictionaryでも同様に実装することができます。
単に逆順にした状態で列挙できればよいのであれば、LINQのReverseメソッドを使うこともできます。 この場合、新たにIComparer<T>を用意する必要がないため、より単純に実現できます。 この方法もSortedList・SortedDictionaryの両方に適用できます。
また、既存のSortedList・SortedDictionaryを降順にするのではなく、始めから降順に並べ替えるように動作するSortedList・SortedDictionaryを作成したい場合は、先の例のReverseComparerをコンストラクタに指定します。
キーに独自型を使用する
構造体やクラスなど、独自に定義した型をSortedList・SortedDictionaryのキーとする場合は、その型の比較を行うIComparer<T>を用意しなければ並べ替えを行うことができません。 次の例は、構造体をキーとしたSortedListを作成・使用する例です。 この方法はSortedListだけでなくSortedDictionaryの場合にも適用できます。
独自型のIComparer<T>を実装する方法の詳細については複合型のソート・複数キーでのソートや大小関係の定義と比較を参照してください。
パフォーマンスの違い
SortedListクラスとSortedDictionaryクラスでは、内部で使用されるデータ構造の違いから使用するメモリの量と挿入・削除の速度にも違いがあらわれます。 Sorted コレクション型によると、両者の違いは次のようになっています。
相違点 | SortedList | SortedDictionary |
---|---|---|
取得操作の速度 | O(log n) | |
挿入操作・削除操作の速度 | 一般にO(n) | 常にO(log n) |
使用するメモリ量 | 比較して少ない | 比較して多い |
SortedListでは、新しい要素が末尾に挿入される場合(挿入の際に並べ替えが発生せず、かつ挿入により内部コレクションのサイズが変わらない場合)、挿入操作の速度は O(1) となります。
以下は、互いに重複しないシャッフルされたキーを使って要素を追加した場合の速度を比較した結果の一例です。
このように、同じ挿入の操作でも速度にかなり大きな差が出ていることが分かると思います。 一方、上記の例のGetShuffledNumbersを次のように変更し、並べ替えが行われないようキーがソートされた状態にすると結果は反転します。
これは、SortedDictionaryでは挿入操作は常にO(log n)であるのに対し、SortedListではキーがソート済みの順で要素が追加されていくために新しい要素は末尾に追加するだけでよく、挿入操作がO(1)となるためです。