System.Collections.Specialized.OrderedDictionaryクラスはインデックスによるアクセスとキーによるアクセスの両方をサポートするディクショナリ(ハッシュテーブル)です。 OrderedDictionaryに類似したディクショナリを構成するクラスとしてHashtableやDictionary<TKey, TValue>、SortedDictionary<TKey, TValue>がありますが、これらのクラスと比較するとOrderedDictionaryには次のような違いがあります。
特徴 | OrderedDictionary | Hashtable | Dictionary | SortedDictionary |
---|---|---|---|---|
インデックスによるアクセス | できる | できない | できない | できる |
列挙したときの順序 | 要素を追加した順 | (不定) | (不定) | ソートされた順序 |
ジェネリック | 非ジェネリック型 | ジェネリック型 | ||
キー/値のペアを格納する型 | DictionaryEntry | KeyValuePair |
OrderedDictionaryは、インデックスによるアクセスができるディクショナリが必要な場合、特に追加した順序を維持するようなディクショナリが必要な場合に有用なコレクションクラスです。 ただし、OrderedDictionaryは非ジェネリックなコレクションクラスであるため、安全に扱うには注意を要します。
追加したときの順序が維持されることが重要で、キーによるアクセスが不要なのであればList<KeyValuePair<TKey, TValue>>
、インデックスによるアクセスができれば列挙した時の順序は特に意識しないのであればSortedDictionary<TKey, TValue>
によって代用することができます。
OrderedDictionary
OrderedDictionaryはインデックスによるアクセスが可能なディクショナリです。 名前の通り順序を持ったディクショナリで、要素を追加すると個々の要素に追加した順でインデックスが割り振られます。 OrderedDictionaryに格納される値は、キーとインデックスの両方で参照できるとも言えます。
インデックス | キー | 値 |
---|---|---|
0 | "foo" | 36 |
1 | "bar" | 7 |
2 | "foo" | 42 |
: : |
: : |
: : |
OrderedDictionaryへの要素の追加・設定はHashtableクラスやDictionaryクラスと同様に、インデクサあるいはAddメソッドによって行います。 一方、キーだけでなくインデックスによって対応する値にアクセスできる点、foreachなどによって列挙した場合にインデックスの順(=追加された順)で列挙されることがクラスの動作として保証されている点が、HashtableやDictionaryとは異なります。
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]);
}
}
}
Imports System
Imports System.Collections ' DictionaryEntryを使うのに必要
Imports System.Collections.Specialized
Class Sample
Shared Sub Main()
' インスタンスの作成
Dim dict As 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に変更
' キーとそれに対応する値を列挙する
For Each pair As DictionaryEntry In dict
Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
Next
' インデックスとそれに対応する値を列挙する
For index As Integer = 0 To dict.Count - 1
Console.WriteLine("{0} => {1}", index, dict(index))
Next
End Sub
End Class
foo => 36 bar => 7 baz => 42 0 => 36 1 => 7 2 => 42
Dictionaryクラスでは、列挙される際の順序は未定義とされていて、要素が追加された順序になるとは限らない旨が記載されています。 詳しくはジェネリックコレクション(2) Dictionary §.要素の列挙と順序を参照してください。
インデックスを指定した挿入・削除
OrderedDictionaryでは個々の要素がインデックスを持つことから、インデックスを指定することによって位置を指定した挿入・削除を行うことができます。
挿入 (Insert)
Insertメソッドを使うと、インデックスで指定した位置へ要素を挿入することができます。 この操作はインデックスを割り当てた上での追加と見ることもできます。 インデックスとして現在の要素数(=最後のインデックス+1)を指定すれば、末尾へと挿入することができます。 それを超える範囲に追加しようとした場合は例外ArgumentOutOfRangeExceptionがスローされます。
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);
}
}
}
Imports System
Imports System.Collections
Imports System.Collections.Specialized
Class Sample
Shared Sub Main()
Dim dict As 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)
For Each pair As DictionaryEntry In dict
Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
Next
End Sub
End Class
foo => 16 qux => 7 bar => 72 baz => 42 quux => 89
削除 (RemoveAt)
RemoveAtメソッドを使うと、インデックスで指定した位置にある要素を削除することができます。 要素の削除を行うと、それに伴い要素に割り当てられるインデックスも変わります。 削除を行っても追加された際の前後関係は維持されますが、インデックスは変化します。
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]);
}
}
}
Imports System
Imports System.Collections
Imports System.Collections.Specialized
Class Sample
Shared Sub Main()
Dim dict As New OrderedDictionary()
dict.Add("foo", 16)
dict.Add("bar", 72)
dict.Add("baz", 42)
' インデックス1にある要素(キー"bar"の要素)を削除する
dict.RemoveAt(1)
For Each pair As DictionaryEntry In dict
Console.WriteLine("{0} => {1}", pair.Key, pair.Value)
Next
For index As Integer = 0 To dict.Count - 1
Console.WriteLine("{0} => {1}", index, dict(index))
Next
End Sub
End Class
foo => 16 baz => 42 0 => 16 1 => 42
インデックスに対応するキーの取得
OrderedDictionary.KeysプロパティはICollectionとなっているため、このプロパティを使ってインデックスに対応するキーを取得するといったことができません。
こういった操作を行う場合は、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]);
}
}
}
Imports System
Imports System.Collections
Imports System.Collections.Specialized
Class Sample
Shared Sub Main()
Dim dict As New OrderedDictionary()
dict.Add("foo", 16)
dict.Add("bar", 72)
dict.Add("baz", 42)
' このようにしてインデックスに対応するキーを取得することはできない
'Dim key As String = dict.Keys(0)
' OrderedDictionaryのキーを配列にコピーする
Dim keys(dict.Count - 1) As String
dict.Keys.CopyTo(keys, 0)
' インデックスを使ってキーの配列を列挙する
Console.WriteLine("[Keys]")
For index As Integer = 0 To keys.Length - 1
Console.WriteLine("{0} => {1}", index, keys(index))
Next
End Sub
End Class
[Keys] 0 => foo 1 => bar 2 => baz
IOrderedDictionaryインターフェイス
OrderedDictionaryはIOrderedDictionaryインターフェイスを実装しています。 このインターフェイスはIDictionaryインターフェイスを継承しています。
IOrderedDictionaryには、IDictionaryの持つ機能に加えて、インデックスおよびキーを指定することができるインデクサ、Insertメソッド、RemoveAtメソッドの定義が追加されています。
.NET Frameworkのクラスライブラリでは、IOrderedDictionaryインターフェイスが実装されているクラスはOrderedDictionary以外に存在せず、System.Web.dllの一部で戻り値や引数として周辺でIOrderedDictionaryが使われている程度です。
ジェネリックバージョンのOrderedDictionary
Hashtableのジェネリック版として対応するDictionary<TKey, TValue>のように、OrderedDictionaryのジェネリック版として対応するようなOrderedDictionary<TKey, TValue>といったクラスは.NET Framework 4.6時点でも存在しません。 必要な場合は独自に実装する必要があります。
このようなジェネリックバージョンのOrderedDictionaryを実装した具体例をジェネリック版OrderedDictionaryにて掲載しています。