System.Collections.Specialized.OrderedDictionaryクラスはインデックスによるアクセスとキーによるアクセスの両方をサポートするディクショナリ(ハッシュテーブル)です。 OrderedDictionaryに類似したディクショナリを構成するクラスとしてHashtableDictionary<TKey, TValue>SortedDictionary<TKey, TValue>がありますが、これらのクラスと比較するとOrderedDictionaryには次のような違いがあります。

ディクショナリを構成する各クラスの特徴
特徴 OrderedDictionary Hashtable Dictionary SortedDictionary
インデックスによるアクセス できる できない できない できる
列挙したときの順序 要素を追加した順 (不定) (不定) ソートされた順序
ジェネリック 非ジェネリック型 ジェネリック型
キー/値のペアを格納する型 DictionaryEntry KeyValuePair

OrderedDictionaryは、インデックスによるアクセスができるディクショナリが必要な場合、特に追加した順序を維持するようなディクショナリが必要な場合に有用なコレクションクラスです。 ただし、OrderedDictionaryは非ジェネリックなコレクションクラスであるため、安全に扱うには注意を要します。

追加したときの順序が維持されることが重要で、キーによるアクセスが不要なのであればList<KeyValuePair<TKey, TValue>>、インデックスによるアクセスができれば列挙した時の順序は特に意識しないのであればSortedDictionary<TKey, TValue>によって代用することができます。

§1 OrderedDictionary

OrderedDictionaryはインデックスによるアクセスが可能なディクショナリです。 名前の通り順序を持ったディクショナリで、要素を追加すると個々の要素に追加した順でインデックスが割り振られます。 OrderedDictionaryに格納されるは、キーインデックスの両方で参照できるとも言えます。

OrderedDictionaryのイメージ
インデックス キー
0 "foo" 36
1 "bar" 7
2 "foo" 42
:
:
:
:
:
:

OrderedDictionaryへの要素の追加・設定はHashtableクラスやDictionaryクラスと同様に、インデクサあるいはAddメソッドによって行います。 一方、キーだけでなくインデックスによって対応する値にアクセスできる点、foreachなどによって列挙した場合にインデックスの順(=追加された順)で列挙されることがクラスの動作として保証されている点が、HashtableやDictionaryとは異なります。

OrderedDictionaryへの要素の追加、OrderedDictionaryでの要素の列挙
using System;
using System.Collections; // DictionaryEntryを使うのに必要
using System.Collections.Specialized;

class Sample {
  static void Main()
  {
    // インスタンスの作成
    var dict = new OrderedDictionary();

    // 要素の追加
    dict.Add("foo", 16); // インデックス0の要素として追加される
    dict.Add("bar", 72); // インデックス1の要素として追加される

    // 要素の設定
    dict["baz"] = 42; // 新たにキー"baz"で要素を追加、インデックス2の要素として追加される
    dict["foo"] = 36; // 既存のキー"foo"に対応する値を変更

    dict[1] = 7; // インデックス1(キー"bar")に対応する値を7に変更

    // キーとそれに対応する値を列挙する
    foreach (DictionaryEntry pair in dict) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }

    // インデックスとそれに対応する値を列挙する
    for (var index = 0; index < dict.Count; index++) {
      Console.WriteLine("{0} => {1}", index, dict[index]);
    }
  }
}
実行結果
foo => 36
bar => 7
baz => 42
0 => 36
1 => 7
2 => 42

Dictionaryクラスでは、列挙される際の順序は未定義とされていて、要素が追加された順序になるとは限らない旨が記載されています。 詳しくはジェネリックコレクション(2) Dictionary §.要素の列挙と順序を参照してください。



§2 インデックスを指定した挿入・削除

OrderedDictionaryでは個々の要素がインデックスを持つことから、インデックスを指定することによって位置を指定した挿入・削除を行うことができます。

§2.1 挿入 (Insert)

Insertメソッドを使うと、インデックスで指定した位置へ要素を挿入することができます。 この操作はインデックスを割り当てた上での追加と見ることもできます。 インデックスとして現在の要素数(=最後のインデックス+1)を指定すれば、末尾へと挿入することができます。 それを超える範囲に追加しようとした場合は例外ArgumentOutOfRangeExceptionがスローされます。

Insertメソッドを使ってOrderedDictionaryの指定した位置へ要素を追加する
using System;
using System.Collections;
using System.Collections.Specialized;

class Sample {
  static void Main()
  {
    var dict = new OrderedDictionary();

    dict.Add("foo", 16);
    dict.Add("bar", 72);
    dict.Add("baz", 42);

    // インデックス1にキー"qux"・値7の要素を挿入する
    dict.Insert(1, "qux", 7);

    // 末尾に要素を挿入(追加)する
    dict.Insert(dict.Count, "quux", 89);

    foreach (DictionaryEntry pair in dict) {
      Console.WriteLine("{0} => {1}", pair.Key, pair.Value);
    }
  }
}
実行結果
foo => 16
qux => 7
bar => 72
baz => 42
quux => 89

§2.2 削除 (RemoveAt)

RemoveAtメソッドを使うと、インデックスで指定した位置にある要素を削除することができます。 要素の削除を行うと、それに伴い要素に割り当てられるインデックスも変わります。 削除を行っても追加された際の前後関係は維持されますが、インデックスは変化します。

RemoveAtメソッドを使ってOrderedDictionaryの指定した位置にある要素を削除する
using System;
using System.Collections;
using System.Collections.Specialized;

class Sample {
  static void Main()
  {
    var dict = new OrderedDictionary();

    dict.Add("foo", 16);
    dict.Add("bar", 72);
    dict.Add("baz", 42);

    // インデックス1にある要素(キー"bar"の要素)を削除する
    dict.RemoveAt(1);

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

    for (var index = 0; index < dict.Count; index++) {
      Console.WriteLine("{0} => {1}", index, dict[index]);
    }
  }
}
実行結果
foo => 16
baz => 42
0 => 16
1 => 42

§3 インデックスに対応するキーの取得

OrderedDictionary.KeysプロパティICollectionとなっているため、このプロパティを使ってインデックスに対応するキーを取得するといったことができません。

こういった操作を行う場合は、Keysプロパティの内容をCopyToメソッドによっていったん配列へとコピーしてから行う必要があります。

Keys.CopyToメソッドを使ってキーを配列にコピーし、インデックスに対応するキーを取得する
using System;
using System.Collections;
using System.Collections.Specialized;

class Sample {
  static void Main()
  {
    var dict = new OrderedDictionary();

    dict.Add("foo", 16);
    dict.Add("bar", 72);
    dict.Add("baz", 42);

    // このようにしてインデックスに対応するキーを取得することはできない
    //var key = dict.Keys[0];

    // OrderedDictionaryのキーを配列にコピーする
    var keys = new string[dict.Count];

    dict.Keys.CopyTo(keys, 0);

    // インデックスを使ってキーの配列を列挙する
    Console.WriteLine("[Keys]");

    for (var index = 0; index < keys.Length; index++) {
      Console.WriteLine("{0} => {1}", index, keys[index]);
    }
  }
}
実行結果
[Keys]
0 => foo
1 => bar
2 => baz

§4 IOrderedDictionaryインターフェイス

OrderedDictionaryはIOrderedDictionaryインターフェイスを実装しています。 このインターフェイスはIDictionaryインターフェイスを継承しています。

IOrderedDictionaryには、IDictionaryの持つ機能に加えて、インデックスおよびキーを指定することができるインデクサ、InsertメソッドRemoveAtメソッドの定義が追加されています。

.NET Frameworkのクラスライブラリでは、IOrderedDictionaryインターフェイスが実装されているクラスはOrderedDictionary以外に存在せず、System.Web.dllの一部で戻り値や引数として周辺でIOrderedDictionaryが使われている程度です。

§5 ジェネリックバージョンのOrderedDictionary

Hashtableのジェネリック版として対応するDictionary<TKey, TValue>のように、OrderedDictionaryのジェネリック版として対応するようなOrderedDictionary<TKey, TValue>といったクラスは.NET Framework 4.6時点でも存在しません。 必要な場合は独自に実装する必要があります。

このようなジェネリックバージョンのOrderedDictionaryを実装した具体例をジェネリック版OrderedDictionaryにて掲載しています。