.NET 9から、要素(キーとバリュー)の追加順序を保持するジェネリックなDictionary、OrderedDictionary<TKey, TValue>
が追加されました。
このOrderedDictionary<TKey, TValue>
クラスは、「キーによる高速な検索」と「順序を維持したコレクション」の両方を実現したいという時に嬉しいクラスです。
実は、.NET Frameworkには昔から、非ジェネリックなOrderedDictionaryがあったのですが、ジェネリックな順序を保持するDictionaryなクラスはなかなか公式ライブラリに登場しませんでした。
従来のSortedDictionary<TKey, TValue>
と違い・
// もともとあったSortedDictionary<TKey, TValue>だと・・・
SortedDictionary<string, string> dictionary = new();
dictionary.Add("KeyC", "ValueC");
dictionary.Add("KeyA", "ValueA");
dictionary.Add("KeyB", "ValueB");
// 従来のSortedDictionary<TKey, TValue>だと、追加順序は保持されず次のように表示される。
// [KeyA, ValueA]
// [KeyB, ValueB]
// [KeyC, ValueC]
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
OrderedDictionary<TKey, TValue>
を列挙すると、追加した順序を保持します。
// .NET 9から追加されたOrderedDictionary<TKey, TValue>だと・・・
OrderedDictionary<string, string> dictionary = new();
dictionary.Add("KeyC", "ValueC");
dictionary.Add("KeyA", "ValueA");
dictionary.Add("KeyB", "ValueB");
// 従来のOrderedDictionary<TKey, TValue>だと、追加順序が保持されて次のように表示される。
// [KeyC, ValueA]
// [KeyA, ValueC]
// [KeyB, ValueB]
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
なお、コレクション初期化子は内部でAddメソッドを読んでいるので、↑と↓は等価です。
OrderedDictionary<string, string> dictionary = new()
{
{ "KeyC", "ValueC" },
{ "KeyA", "ValueA" },
{ "KeyB", "ValueB" }
};
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
「キーによる高速な検索」と「順序を維持したコレクション」の両方を実現したい場合は、OrderedDictionary<TKey, TValue>
が便利です。
以下に、OrderedDictionary<TKey, TValue>
の特徴的なメソッドの利用例を示します。
GetAtで、指定したインデックスのKeyValuePair<TKey, TValue>
を取得できます。
OrderedDictionary<string, string> dictionary = new()
{
{ "Key0", "Value0" },
{ "Key1", "Value1" },
{ "Key2", "Value2" }
};
var index1KeyValuePair = dictionary.GetAt(1);
// [Key1, Value1]と表示される。(インデックス0始まりで1個目のKeyValuePair要素)
Console.WriteLine(index1KeyValuePair);
RemoveAtで、指定したインデックスのKeyValuePair<TKey, TValue>
を取り除けます。
OrderedDictionary<string, string> dictionary = new()
{
{ "Key0", "Value0" },
{ "Key1", "Value1" },
{ "Key2", "Value2" }
};
// インデックス1の要素を削除
dictionary.RemoveAt(1);
// 次のように表示される。
// [Key0, Value0]
// [Key2, Value2]
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
InsertAtで、指定したインデックスにキーとバリューの要素を挿入できます。
OrderedDictionary<string, string> dictionary = new()
{
{ "Key0", "Value0" },
{ "Key1", "Value1" },
{ "Key2", "Value2" }
};
// インデックス1に、要素をインサート
dictionary.Insert(1, "InsertedKey", "InsertedValue");
// [Key0, Value0]
// [InsertedKey, InsertedValue]
// [Key1, Value1]
// [Key2, Value2]
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
SetAtで指定したインデックスにキーとバリュー、もしくはバリューを上書きできます。
OrderedDictionary<string, string> dictionary = new()
{
{ "Key0", "Value0" },
{ "Key1", "Value1" },
{ "Key2", "Value2" }
};
// インデックス0のキーとバリューをセット
dictionary.SetAt(0, "KeyReplaced0", "ValueReplaced0");
// インデックス2のバリューだけセット
dictionary.SetAt(2, "ValueReplaced2");
// [KeyReplaced0, ValueReplaced0]
// [Key1, Value1]
// [Key2, ValueReplaced2]
foreach (var keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair);
}
IndexOfで指定したキーのインデックスを探索できます。
OrderedDictionary<string, string> dictionary = new()
{
{ "Key0", "Value0" },
{ "Key1", "Value1" },
{ "Key2", "Value2" }
};
// "Key0"のインデックスを探索
var indexOfKey0 = dictionary.IndexOf("Key0");
// 0
Console.WriteLine(indexOfKey0);
ちなみに、OrderedDictionary<TKey, TValue>
は、IList<KeyValuePair<TKey, TValue>>
を実装しています。
OrderedDictionary<string, string> orderedDictionary = new OrderedDictionary<string, string>
{
{ "KeyA", "ValueA" },
{ "KeyB", "ValueB" },
{ "KeyC", "ValueC" }
};
// OrderedDictionary<TKey, TValue>は、IList<KeyValuePair<TKey, TValue>>を実装している
// だから当然、これはOK
IList<KeyValuePair<string, string>> list = orderedDictionary;
// IList<KeyValuePair<string, string>>、intでの[]アクセスができる
KeyValuePair<string, string> elementIndex0 = list[1];
// OrderedDictionary<string, string>だと、TKeyであるstringでの[]アクセス
string valueWithKeyA = orderedDictionary["KeyA"];
普通のDictionary<TKey, TValue>
は、当然IList<KeyValuePair<TKey, TValue>>
を実装していません。それはそう。