§1 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クラスを使います。



§2 基本操作

§2.1 インスタンスの作成・要素の取得・設定・列挙

インスタンスの作成、要素の取得と設定、要素の列挙などの操作はDictionaryクラスを使う場合と変わりありません。 foreach文で列挙を行う際にソートされた状態で列挙される点のみが異なります。

SortedList
SortedListへの要素の設定と列挙時の順序
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    SortedList<string, int> list = new SortedList<string, int>();

    // 要素の設定
    list["Dave"]    = 1;
    list["Alice"]   = 2;
    list["Bob"]     = 3;
    list["Eve"]     = 4;
    list["Charlie"] = 5;

    // 格納されている要素の列挙
    foreach (KeyValuePair<string, int> pair in list) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
  }
}
SortedDictionary
SortedDictionaryへの要素の設定と列挙時の順序
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>();

    // 要素の設定
    dict["Dave"]    = 1;
    dict["Alice"]   = 2;
    dict["Bob"]     = 3;
    dict["Eve"]     = 4;
    dict["Charlie"] = 5;

    // 格納されている要素の列挙
    foreach (KeyValuePair<string, int> pair in dict) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
Alice    => 2
Bob      => 3
Charlie  => 5
Dave     => 1
Eve      => 4

Keysプロパティを列挙した場合も同様に、キーがソートされた状態で列挙されます。 Valuesプロパティを列挙した場合は、ソートされたキーと同じ順序でキーに対応する値が列挙されます(値単独でソートされた順序にはなりません)。

SortedList
SortedListでKeys・Valuesを列挙した場合の順序
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    SortedList<string, int> list = new SortedList<string, int>();

    // 要素の設定
    list["Dave"]    = 1;
    list["Alice"]   = 2;
    list["Bob"]     = 3;
    list["Eve"]     = 4;
    list["Charlie"] = 5;

    // 格納されているキーの列挙
    foreach (string key in list.Keys) {
      Console.WriteLine(key);
    }

     // 格納されている値の列挙
    foreach (int val in list.Values) {
      Console.WriteLine(val);
    }
  }
}
SortedDictionary
SortedDictionaryでKeys・Valuesを列挙した場合の順序
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>();

    // 要素の設定
    dict["Dave"]    = 1;
    dict["Alice"]   = 2;
    dict["Bob"]     = 3;
    dict["Eve"]     = 4;
    dict["Charlie"] = 5;

    // 格納されているキーの列挙
    foreach (string key in dict.Keys) {
      Console.WriteLine(key);
    }

     // 格納されている値の列挙
    foreach (int val in dict.Values) {
      Console.WriteLine(val);
    }
  }
}
実行結果
Alice
Bob
Charlie
Dave
Eve
2
3
5
1
4

§2.2 コレクション初期化子

Dictionaryの場合と同様、SortedList・SortedDictionaryでもコレクション初期化子を使ってインスタンスの作成と要素の追加を同時に行うことができます。 コレクション初期化子はC# 3.0(Visual C# 2008)以降、VB 10(Visual Basic 2010)以降でサポートされます。

SortedList
コレクション初期化子を用いて要素を追加した状態のSortedListを作成する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // コレクション初期化子を使ってインスタンスの作成と要素の追加を行う
    SortedList<string, int> list = new SortedList<string, int>() {
      {"Dave",    1},
      {"Alice",   2},
      {"Bob",     3},
      {"Eve",     4},
      {"Charlie", 5},
    };

    Console.WriteLine("Count = {0}", list.Count);
    Console.WriteLine("Eve => {0}", list["Eve"]);
  }
}
SortedDictionary
コレクション初期化子を用いて要素を追加した状態のSortedDictionaryを作成する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    // コレクション初期化子を使ってインスタンスの作成と要素の追加を行う
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>() {
      {"Dave",    1},
      {"Alice",   2},
      {"Bob",     3},
      {"Eve",     4},
      {"Charlie", 5},
    };

    Console.WriteLine("Count = {0}", dict.Count);
    Console.WriteLine("Eve => {0}", dict["Eve"]);
  }
}
実行結果
Count = 5
Eve => 4

§2.3 存在しないキーを参照した場合・既にキーが存在する場合

存在しないキーを参照した場合の動作はDictionaryと同様で、例外KeyNotFoundExceptionがスローされます。

SortedList
SortedListに存在しないキーを参照しようとした場合
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["foo"] = 16;
    list["bar"] = 72;

    // 存在するキーに対応する値を取得
    Console.WriteLine("foo = {0}", list["foo"]);
    Console.WriteLine("bar = {0}", list["bar"]);

    // 存在しないキー"hoge"に対応する値を取得
    Console.WriteLine("hoge = {0}", list["hoge"]);
  }
}
SortedDictionary
SortedDictionaryに存在しないキーを参照しようとした場合
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>();

    dict["foo"] = 16;
    dict["bar"] = 72;

    // 存在するキーに対応する値を取得
    Console.WriteLine("foo = {0}", dict["foo"]);
    Console.WriteLine("bar = {0}", dict["bar"]);

    // 存在しないキー"hoge"に対応する値を取得
    Console.WriteLine("hoge = {0}", dict["hoge"]);
  }
}
実行結果
foo = 16
bar = 72

ハンドルされていない例外: System.Collections.Generic.KeyNotFoundException: 指定されたキーはディレクトリ内に存在しませんでした。
   場所 System.Collections.Generic.SortedDictionary`2.get_Item(TKey key)
   場所 Sample.Main()

キーを指定した上書き・追加の動作もDictionaryと同様です。 インデクサで既に存在するキーを指定した場合は値が上書きされ、Addメソッドで既に存在するキーを指定した場合は例外ArgumentExceptionがスローされます。

SortedList
既にSortedListに存在するキーを指定した場合
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["foo"] = 16;

    Console.WriteLine("foo = {0}", list["foo"]);

    // 既に存在するキーを指定して値を上書き
    list["foo"] = 72;

    Console.WriteLine("foo = {0}", list["foo"]);

    // 既に存在するキーを指定して値を追加
    list.Add("foo", 42);

    Console.WriteLine("foo = {0}", list["foo"]);
  }
}
SortedDictionary
既にSortedDictionaryに存在するキーを指定した場合
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>();

    dict["foo"] = 16;

    Console.WriteLine("foo = {0}", dict["foo"]);

    // 既に存在するキーを指定して値を上書き
    dict["foo"] = 72;

    Console.WriteLine("foo = {0}", dict["foo"]);

    // 既に存在するキーを指定して値を追加
    dict.Add("foo", 42);

    Console.WriteLine("foo = {0}", dict["foo"]);
  }
}
実行結果
foo = 16
foo = 72

ハンドルされていない例外: System.ArgumentException: 同一のキーを含む項目が既に追加されています。
   場所 System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   場所 System.Collections.Generic.TreeSet`1.AddIfNotPresent(T item)
   場所 System.Collections.Generic.SortedDictionary`2.Add(TKey key, TValue value)
   場所 Sample.Main()

§2.4 要素の検索・削除・存在チェック

Dictionaryクラスと同様、ContainsKeyメソッドやTryGetValueメソッドなどを使うことができます。

SortedList
ContainsKeyメソッド・TryGetValueメソッドを使ってSortedList内の要素を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["foo"] = 16;
    list["bar"] = 72;
    list["baz"] = 42;

    // キー"foo"を持つ要素があるかどうか
    Console.WriteLine(list.ContainsKey("foo"));

    // 値9を持つ要素があるかどうか
    Console.WriteLine(list.ContainsValue(9));

    // キー"bar"の要素を削除
    list.Remove("bar");

    foreach (KeyValuePair<string, int> pair in list) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }

    // キー"baz"に該当する値を取得
    int valueOfBaz;

    if (list.TryGetValue("baz", out valueOfBaz)) {
      Console.WriteLine("value of 'baz': {0}", valueOfBaz);
    }
    else {
      Console.WriteLine("key not found");
    }

    // キー"hoge"に該当する値を取得
    int valueOfHoge;

    if (list.TryGetValue("hoge", out valueOfHoge)) {
      Console.WriteLine("value of 'hoge': {0}", valueOfHoge);
    }
    else {
      Console.WriteLine("key not found");
    }
  }
}
SortedDictionary
ContainsKeyメソッド・TryGetValueメソッドを使ってSortedDictionary内の要素を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>();

    dict["foo"] = 16;
    dict["bar"] = 72;
    dict["baz"] = 42;

    // キー"foo"を持つ要素があるかどうか
    Console.WriteLine(dict.ContainsKey("foo"));

    // 値9を持つ要素があるかどうか
    Console.WriteLine(dict.ContainsValue(9));

    // キー"bar"の要素を削除
    dict.Remove("bar");

    foreach (KeyValuePair<string, int> pair in dict) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }

    // キー"baz"に該当する値を取得
    int valueOfBaz;

    if (dict.TryGetValue("baz", out valueOfBaz)) {
      Console.WriteLine("value of 'baz': {0}", valueOfBaz);
    }
    else {
      Console.WriteLine("key not found");
    }

    // キー"hoge"に該当する値を取得
    int valueOfHoge;

    if (dict.TryGetValue("hoge", out valueOfHoge)) {
      Console.WriteLine("value of 'hoge': {0}", valueOfHoge);
    }
    else {
      Console.WriteLine("key not found");
    }
  }
}
実行結果
True
False
baz => 42
foo => 16
value of 'baz': 42
key not found

§3 SortedListのみでサポートされる操作

SortedListとSortedDictionaryの相違点の一つとして、SortedListではインデックスを使った操作ができるという点が挙げられます。 以下の操作はSortedListのみで提供されるもので、SortedDictionaryでは提供されないものです。

§3.1 インデックスを指定した操作

SortedListクラスでは、インデックスを指定してキー・値へアクセスすることができます。 KeysプロパティおよびValuesプロパティでは、インデックスを指定することで個々の要素のキーと値を取得できます。

インデックスを指定してSortedList内のキー・値を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["Dave"]    = 1;
    list["Alice"]   = 2;
    list["Bob"]     = 3;
    list["Eve"]     = 4;
    list["Charlie"] = 5;

    for (int i = 0; i < list.Count; i++) {
      // インデックスを指定してキーと値を取得
      Console.WriteLine("[{0}] {1,-8} => {2}", i, list.Keys[i], list.Values[i]);
    }
  }
}
実行結果
[0] Alice    => 2
[1] Bob      => 3
[2] Charlie  => 5
[3] Dave     => 1
[4] Eve      => 4

なお、Keysプロパティ・Valuesプロパティを使ってインデックスを指定した値・キーの設定は行うことはできません。


SortedListでは、ソートされた順に従って各要素にインデックスが割り振られます(要素が追加された順ではありません)。 インデックス0の要素は、並べ替えた際に最初に位置する要素となります。 要素の追加や削除が行われる度に並べ替えられるため、要素に割り振られていたインデックスが変わることもあります。

要素の追加・削除した場合のSortedList内のインデックスの変化
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["Dave"]  = 1;
    list["Bob"]   = 3;

    // インデックス0のキーと値を取得
    Console.WriteLine("{0,-8} => {1}", list.Keys[0], list.Values[0]);

    // 要素を削除
    list.Remove("Bob");

    // インデックス0のキーと値を取得
    Console.WriteLine("{0,-8} => {1}", list.Keys[0], list.Values[0]);

    // 要素を追加
    list.Add("Alice", 2);

    // インデックス0のキーと値を取得
    Console.WriteLine("{0,-8} => {1}", list.Keys[0], list.Values[0]);
  }
}
実行結果
Bob      => 3
Dave     => 1
Alice    => 2

キーと値のペアを格納するコレクションで、かつ要素が追加された順でインデックスが割り振られるようなコレクションが必要な場合は、OrderedDictionaryクラスを使うことができます。 ただし、このコレクションは非ジェネリックコレクションです。 ジェネリック版OrderedDictionaryではジェネリック版のOrderedDictionaryを実装したものを掲載しています。

§3.2 キーまたは値からインデックスを取得する

キーや値からインデックスを取得したい場合は、IndexOfKeyメソッドおよびIndexOfValueメソッドを使うことでキー・値のインデックスを取得することができます。 SortedListに該当するキー・値が存在しない場合は-1が返されます。

IndexOfKeyメソッド・IndexOfValueメソッドを使ってSortedListにある指定したインデックスを持つキー・値を取得する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["Dave"]    = 1;
    list["Alice"]   = 2;
    list["Bob"]     = 3;
    list["Eve"]     = 4;
    list["Charlie"] = 5;

    // インデックス・キー・値の一覧を表示
    for (int i = 0; i < list.Count; i++) {
      Console.WriteLine("[{0}] {1,-8} => {2}", i, list.Keys[i], list.Values[i]);
    }

    Console.WriteLine();

    // キー"Charlie"を持つ要素のインデックスを取得
    Console.WriteLine("IndexOfKey 'Charlie': {0}", list.IndexOfKey("Charlie"));

    // 値2を持つ要素のインデックスを取得
    Console.WriteLine("IndexOfValue 2: {0}", list.IndexOfValue(2));

    // 値42を持つ要素のインデックスを取得
    Console.WriteLine("IndexOfValue 42: {0}", list.IndexOfValue(42));
  }
}
実行結果
[0] Alice    => 2
[1] Bob      => 3
[2] Charlie  => 5
[3] Dave     => 1
[4] Eve      => 4

IndexOfKey 'Charlie': 2
IndexOfValue 2: 0
IndexOfValue 42: -1

§3.3 インデックスを指定して要素を削除する

SortedListでは、Removeメソッドによるキーを指定した要素の削除の他に、RemoveAtメソッドによるインデックスを指定した要素の削除を行うことができます。

RemoveAtメソッドを使って指定したインデックスを持つSortedList内の要素を削除する
using System;
using System.Collections.Generic;

class Sample {
  static void Main()
  {
    SortedList<string, int> list = new SortedList<string, int>();

    list["Dave"]    = 1;
    list["Alice"]   = 2;
    list["Bob"]     = 3;
    list["Eve"]     = 4;
    list["Charlie"] = 5;

    for (int i = 0; i < list.Count; i++) {
      Console.WriteLine("[{0}] {1,-8} => {2}", i, list.Keys[i], list.Values[i]);
    }

    Console.WriteLine();

    // インデックス3の要素を削除
    list.RemoveAt(3);

    for (int i = 0; i < list.Count; i++) {
      Console.WriteLine("[{0}] {1,-8} => {2}", i, list.Keys[i], list.Values[i]);
    }
  }
}
実行結果
[0] Alice    => 2
[1] Bob      => 3
[2] Charlie  => 5
[3] Dave     => 1
[4] Eve      => 4

[0] Alice    => 2
[1] Bob      => 3
[2] Charlie  => 5
[3] Eve      => 4

§4 キー比較・ソート順のカスタマイズ

SortedList・SortedDictionaryにはSortメソッドやReverseメソッドなどコレクションに格納されている要素の順序を並べ替えるメソッドは用意されていません。 そのため、一度インスタンスを作成すると後から並べ替え順序を変更することはできません。

SortedList・SortedDictionaryでは、コンストラクタで適切なIComparer<T>インターフェイスを指定することでキーの比較や並べ替え順序を独自に定義したものに変更することができます。

以下の例で使用するIComparer<T>インターフェイスなど、比較処理に関する詳細については以下のページも合わせてご覧ください。

§4.1 独自の並べ替え順序を定義する

次の例は、並べ替えの順序を独自に定義してSortedList・SortedDictionaryでの並べ替え順をカスタマイズするものです。 大文字小文字の違いを無視し、アルファベット順とは逆順(Z→Aの順)になるようにソートするIComparer<string>を実装したクラスCaseInsensitiveReverseStringComparerを作成し、それをSortedList・SortedDictionaryのコンストラクタに指定することで独自に定義した並べ替え順序となるようにしています。

IComparer<string>を実装したクラスを作成してSortedList・SortedDictionaryの並べ替え順序を独自に定義する
using System;
using System.Collections.Generic;

// 大文字小文字の違いを無視し、アルファベット順とは逆順にソートするためのIComparer
class CaseInsensitiveReverseStringComparer : IComparer<string> {
  public int Compare(string x, string y)
  {
    // StringComparer.CurrentCultureIgnoreCase.Compareの戻り値の符号を反転して逆順となるような結果を返す
    return -1 * StringComparer.CurrentCultureIgnoreCase.Compare(x, y);
  }
}

class Sample {
  static void Main()
  {
    // 独自に定義した並べ替え順序を実装するIComparerを指定してSortedListを作成
    SortedList<string, int> list = new SortedList<string, int>(new CaseInsensitiveReverseStringComparer());

    list["Bob"]     = 1;
    list["Alice"]   = 2;
    list["Charlie"] = 3;

    list["aLiCe"]   = 16;
    list["BOB"]     = 72;

    Console.WriteLine("[SortedList]");

    foreach (KeyValuePair<string, int> pair in list) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }

    // 独自に定義した並べ替え順序を実装するIComparerを指定してSortedDictionaryを作成
    SortedDictionary<string, int> dict = new SortedDictionary<string, int>(new CaseInsensitiveReverseStringComparer());

    dict["Bob"]     = 1;
    dict["Alice"]   = 2;
    dict["Charlie"] = 3;

    dict["aLiCe"]   = 16;
    dict["BOB"]     = 72;

    Console.WriteLine("[SortedDictionary]");

    foreach (KeyValuePair<string, int> pair in dict) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
[SortedList]
Charlie  => 3
BOB      => 72
aLiCe    => 16
[SortedDictionary]
Charlie  => 3
Bob      => 72
Alice    => 16

実行結果からもわかるように、大文字小文字の違いがあっても同一のキーとして扱われるようになるため、値は上書きされるようになります。

§4.2 降順・逆順にする

SortedList・SortedDictionaryにはReverseメソッドが用意されていないため、コレクションの内容を昇順から降順に並べ替え順序を変えることはできません。 並べ替え順序を変えるには新たに別のインスタンスを作成する必要があります。

次の例では、既存のSortedListのコピーを作成し、内容は同じで並べ替え順序だけを逆にしたインスタンスを作成することにより、逆順にしたSortedListを得ています。 新しいSortedListのインスタンスを作成する際、コンストラクタに既存のSortedListを指定することで同一の内容を持つインスタンスを作成します。 同時に、元のIComparer<T>とは逆の順序に並べ替えるようにしたIComparer<T>をコンストラクタに指定することで逆順に並べ替えるようにしています。

この例で紹介する方法では、ComparerプロパティからIComparer<T>を取得することにより、個別に並べ替え順序を定義することなく単に並べ替え順序を逆にしています。 そのため、キーの型に関わらずどのようなSortedListでも逆順にすることができます。

SortedListをデフォルトの逆順で並べ替える
using System;
using System.Collections.Generic;

// 指定されたIComparer<T>と逆の順序に並べ替えるIComparer<T>の実装
class ReverseComparer<T> : IComparer<T> {
  private IComparer<T> comparer;

  public ReverseComparer(IComparer<T> comparer)
  {
    this.comparer = comparer;
  }

  public int Compare(T x, T y)
  {
    // 指定する引数の順序を逆にすることで、元になったIComparer<T>とは逆の結果を返す
    return comparer.Compare(y, x);
  }
}

class Sample {
  // 内容はそのままで並べ替え順序を逆にしたSortedListを作成するメソッド
  static SortedList<TKey, TValue> CreateReversed<TKey, TValue>(SortedList<TKey, TValue> source)
  {
    // コレクションの内容はsourceと同じもの、並べ替え順序はsource.Comparerで
    // 定義されているものとは逆となるようなインスタンスを返す
    return new SortedList<TKey, TValue>(source, new ReverseComparer<TKey>(source.Comparer));
  }

  static void Main()
  {
    // キーがstringのSortedList
    SortedList<string, int> list1 = new SortedList<string, int>();

    list1["Bob"]     = 1;
    list1["Alice"]   = 2;
    list1["Charlie"] = 3;

    // 逆順にしたものを作成し、内容を表示
    foreach (KeyValuePair<string, int> pair in CreateReversed(list1)) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
    Console.WriteLine();

    // キーがintのSortedList
    SortedList<int, string> list2 = new SortedList<int, string>();

    list2[2] = "Alice";
    list2[1] = "Bob";
    list2[3] = "Charlie";

    // 逆順にしたものを作成し、内容を表示
    foreach (KeyValuePair<int, string> pair in CreateReversed(list2)) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
Charlie  => 3
Bob      => 1
Alice    => 2

3 => Charlie
2 => Alice
1 => Bob

この方法は、SortedDictionaryでも同様に実装することができます。


単に逆順にした状態で列挙できればよいのであれば、LINQのReverseメソッドを使うこともできます。 この場合、新たにIComparer<T>を用意する必要がないため、より単純に実現できます。 この方法もSortedList・SortedDictionaryの両方に適用できます。

SortedListをデフォルトの逆順で列挙する
using System;
using System.Collections.Generic;
using System.Linq;

class Sample {
  static void Main()
  {
    // キーがstringのSortedList
    SortedList<string, int> list1 = new SortedList<string, int>();

    list1["Bob"]     = 1;
    list1["Alice"]   = 2;
    list1["Charlie"] = 3;

    // Reverseメソッドを使ってSortedListを逆順で列挙する
    foreach (KeyValuePair<string, int> pair in list1.Reverse()) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
    Console.WriteLine();

    // キーがintのSortedList
    SortedList<int, string> list2 = new SortedList<int, string>();

    list2[2] = "Alice";
    list2[1] = "Bob";
    list2[3] = "Charlie";

    // Reverseメソッドを使ってSortedListを逆順で列挙する
    foreach (KeyValuePair<int, string> pair in list2.Reverse()) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
Charlie  => 3
Bob      => 1
Alice    => 2

3 => Charlie
2 => Alice
1 => Bob

また、既存のSortedList・SortedDictionaryを降順にするのではなく、始めから降順に並べ替えるように動作するSortedList・SortedDictionaryを作成したい場合は、先の例のReverseComparerをコンストラクタに指定します。

降順に並べ替えるSortedList・SortedDictionaryを作成する
using System;
using System.Collections.Generic;

// 指定されたIComparer<T>と逆の順序に並べ替えるIComparer<T>の実装
class ReverseComparer<T> : IComparer<T> {
  private IComparer<T> comparer;

  public ReverseComparer(IComparer<T> comparer)
  {
    this.comparer = comparer;
  }

  public int Compare(T x, T y)
  {
    // 指定する引数の順序を逆にすることで、元になったIComparer<T>とは逆の結果を返す
    return comparer.Compare(y, x);
  }
}

class Sample {
  static void Main()
  {
    // キーがstringで、降順に並べ替えるSortedListを作成する
    IComparer<string> descendingStringComparer = new ReverseComparer<string>(Comparer<string>.Default);
    SortedList<string, int> list1 = new SortedList<string, int>(descendingStringComparer);

    list1["Bob"]     = 1;
    list1["Alice"]   = 2;
    list1["Charlie"] = 3;

    // 内容を表示
    foreach (KeyValuePair<string, int> pair in list1) {
      Console.WriteLine("{0,-8} => {1}", pair.Key, pair.Value);
    }
    Console.WriteLine();

    // キーがintで、降順に並べ替えるSortedListを作成する
    IComparer<int> descendingIntComparer = new ReverseComparer<int>(Comparer<int>.Default);
    SortedList<int, string> list2 = new SortedList<int, string>(descendingIntComparer);

    list2[2] = "Alice";
    list2[1] = "Bob";
    list2[3] = "Charlie";

    // 内容を表示
    foreach (KeyValuePair<int, string> pair in list2) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
Charlie  => 3
Bob      => 1
Alice    => 2

3 => Charlie
2 => Alice
1 => Bob

§4.3 キーに独自型を使用する

構造体やクラスなど、独自に定義した型をSortedList・SortedDictionaryのキーとする場合は、その型の比較を行うIComparer<T>を用意しなければ並べ替えを行うことができません。 次の例は、構造体をキーとしたSortedListを作成・使用する例です。 この方法はSortedListだけでなくSortedDictionaryの場合にも適用できます。

SortedListのキーに構造体を使用できるようにする
using System;
using System.Collections.Generic;

// SortedListのキーとして使用する構造体
struct Account {
  public string Name;
  public int ID;

  public Account(string name, int id)
  {
    Name = name;
    ID = id;
  }
}

// Account構造体のNameフィールドで並べ替えを行うIComparer<T>
class AccountNameComparer : IComparer<Account> {
  public int Compare(Account x, Account y)
  {
    return string.Compare(x.Name, y.Name);
  }
}

// Account構造体のIDフィールドで並べ替えを行うIComparer<T>
class AccountIDComparer : IComparer<Account> {
  public int Compare(Account x, Account y)
  {
    return x.ID - y.ID;
  }
}

class Sample {
  static void Main()
  {
    // キーにAccount構造体を使用し、Nameフィールドに従って並べ替えるSortedList
    SortedList<Account, string> list1 = new SortedList<Account, string>(new AccountNameComparer());

    list1[new Account("Bob", 1)]      = "bob@example.com";
    list1[new Account("Charlie", 3)]  = "charlie@example.net";
    list1[new Account("Alice", 2)]    = "alice@mail.example.net";

    // 内容を表示
    foreach (KeyValuePair<Account, string> pair in list1) {
      Console.WriteLine("{0}:{1,-8} => {2}", pair.Key.ID, pair.Key.Name, pair.Value);
    }
    Console.WriteLine();

    // キーにAccount構造体を使用し、IDフィールドに従って並べ替えるSortedList
    SortedList<Account, string> list2 = new SortedList<Account, string>(new AccountIDComparer());

    list2[new Account("Bob", 1)]      = "bob@example.com";
    list2[new Account("Charlie", 3)]  = "charlie@example.net";
    list2[new Account("Alice", 2)]    = "alice@mail.example.net";

    // 内容を表示
    foreach (KeyValuePair<Account, string> pair in list2) {
      Console.WriteLine("{0}:{1,-8} => {2}", pair.Key.ID, pair.Key.Name, pair.Value);
    }
  }
}
実行結果
2:Alice    => alice@mail.example.net
1:Bob      => bob@example.com
3:Charlie  => charlie@example.net

1:Bob      => bob@example.com
2:Alice    => alice@mail.example.net
3:Charlie  => charlie@example.net

独自型のIComparer<T>を実装する方法の詳細については複合型のソート・複数キーでのソート大小関係の定義と比較を参照してください。

§5 パフォーマンスの違い

SortedListクラスとSortedDictionaryクラスでは、内部で使用されるデータ構造の違いから使用するメモリの量と挿入・削除の速度にも違いがあらわれます。 Sorted コレクション型によると、両者の違いは次のようになっています。

SortedListとSortedDictionaryの相違点
(nはコレクション内の要素数)
相違点 SortedList SortedDictionary
取得操作の速度 O(log n)
挿入操作・削除操作の速度 一般にO(n) 常にO(log n)
使用するメモリ量 比較して少ない 比較して多い

SortedListでは、新しい要素が末尾に挿入される場合(挿入の際に並べ替えが発生せず、かつ挿入により内部コレクションのサイズが変わらない場合)、挿入操作の速度は O(1) となります。

以下は、互いに重複しないシャッフルされたキーを使って要素を追加した場合の速度を比較した結果の一例です。

検証に使ったコード
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;

class Sample {
  // 0からcountまでの数値をランダムな順番で返すメソッド
  static IEnumerable<int> GetShuffledNumbers(int count)
  {
    var rng = RandomNumberGenerator.Create();
    var bytes = new byte[4];

    return Enumerable.Range(0, count).OrderBy(i => {
      rng.GetBytes(bytes);

      return BitConverter.ToInt32(bytes, 0);
    });
  }

  static void Main()
  {
#if DEBUG
    foreach (var key in GetShuffledNumbers(50)) {
      Console.Write("{0}, ", key);
    }

    Console.WriteLine();

    return;
#endif

    const int keyCount = 100 * 1000;
    const int repeatCount = 3;

    for (var repeat = 0; repeat < repeatCount; repeat++) {
      var keys = GetShuffledNumbers(keyCount).ToArray();

      Console.WriteLine("[{0}]", repeat);

      var list = new SortedList<int, int>();
      var sw1 = Stopwatch.StartNew();

      foreach (var key in keys) {
        list.Add(key, key);
      }

      sw1.Stop();

      Console.WriteLine("{0,-20}(Count = {1}): {2}", "SortedList", list.Count, sw1.Elapsed);

      var dict = new SortedDictionary<int, int>();
      var sw2 = Stopwatch.StartNew();

      foreach (var key in keys) {
        dict.Add(key, key);
      }

      sw2.Stop();

      Console.WriteLine("{0,-20}(Count = {1}): {2}", "SortedDictionary", dict.Count, sw2.Elapsed);

      Console.WriteLine("{0:P2}", sw1.ElapsedTicks / (double)sw2.ElapsedTicks);
    }
  }
}
実行結果の一例
[0]
SortedList          (Count = 100000): 00:00:03.6776776
SortedDictionary    (Count = 100000): 00:00:00.0915454
4,017.33%
[1]
SortedList          (Count = 100000): 00:00:03.7265242
SortedDictionary    (Count = 100000): 00:00:00.0542817
6,865.15%
[2]
SortedList          (Count = 100000): 00:00:04.1011721
SortedDictionary    (Count = 100000): 00:00:00.0748488
5,479.27%

このように、同じ挿入の操作でも速度にかなり大きな差が出ていることが分かると思います。 一方、上記の例のGetShuffledNumbersを次のように変更し、並べ替えが行われないようキーがソートされた状態にすると結果は反転します。

static IEnumerable<int> GetShuffledNumbers(int count)
{
  // ランダムな順番ではなく、ソートが不要な順番で返すようにする
  return Enumerable.Range(0, count);
}
実行結果の一例
[0]
SortedList          (Count = 100000): 00:00:00.0222451
SortedDictionary    (Count = 100000): 00:00:00.0748501
29.72%
[1]
SortedList          (Count = 100000): 00:00:00.0204976
SortedDictionary    (Count = 100000): 00:00:00.0529541
38.71%
[2]
SortedList          (Count = 100000): 00:00:00.0179380
SortedDictionary    (Count = 100000): 00:00:00.0554350
32.36%

これは、SortedDictionaryでは挿入操作は常にO(log n)であるのに対し、SortedListではキーがソート済みの順で要素が追加されていくために新しい要素は末尾に追加するだけでよく、挿入操作がO(1)となるためです。