rksoftware

Visual Studio とか C# とかが好きです

.NET 9 で追加された OrderedDictionary を見る

.NET 9 で System.Collections.Generic.OrderedDictionary が追加されたとのことで、見てみましょう。
github.com

■ Dictionary とは何が違うのか

順序が保持される点が違うはずです。OrderedDictionary だけにあるメソッドを見てみると明確です。

var orderdDictTypeMembers = typeof(System.Collections.Generic.OrderedDictionary<string, object>).GetMembers().Select(m=>m.Name).Distinct().Order().ToArray();
var dictTypeMEmbers = typeof(System.Collections.Generic.Dictionary<string, object>).GetMembers().Select(m => m.Name).Distinct().Order().ToArray();
foreach(var name in orderdDictTypeMembers.Except(dictTypeMEmbers).Intersect(orderdDictTypeMembers))
    Console.WriteLine(name);
// GetAt
// IndexOf
// Insert
// RemoveAt
// SetAt

順序を指定して取得/追加/削除/変更がありますね。

OrderedDictionary があると何がうれしいのか

順序も保持する、高速なキーアクセスでの値の取得、両方やらなくちゃならないのがつらいところ......、という場面でうれしいようです。
もし OrderedDictionary がなかったら、順序保持用に List とキーアクセス用に Dictionary、と二重に保持することになるでしょう。そういうコード、何気に書くこと多くあります。

■ 試してみる

Write(Init(new System.Collections.Generic.OrderedDictionary<string, int>()));
Write(Init(new System.Collections.Generic.Dictionary<string, int>()));
// [c, 0]
// [a, 1]
// [b, 2]
// [c, 0]
// [a, 1]
// [b, 2]

{
    var ordered = new System.Collections.Generic.OrderedDictionary<string, int>();
    Init(ordered);
    ordered.Insert(1, "d", 3);
    Write(ordered);
}
// [c, 0]
// [d, 3]
// [a, 1]
// [b, 2]

static IDictionary<string,int> Init(IDictionary<string, int> d) { d.Add("c",0); d.Add("a", 1); d.Add("b", 2); return d; }
static void Write(IDictionary<string, int> d) { foreach (var m in d) Console.WriteLine(m); }

Dictionary でも add した順になっているのでわかりにくいですが、Insert で追加した場所にちゃんと追加されていますね。

■ 速度に違いはあるのか

Orderされている順でキーアクセスの速度に違いが出るのか、雑に動かしてみます。

var ordered = new System.Collections.Generic.OrderedDictionary<string, int> { { "c", 0 }, { "a", 1 }, { "b", 2 } };
Get(ordered, "a");  // 259760
Get(ordered, "b");  // 90231
Get(ordered, "c");  // 89316
Get(ordered, "a");  // 76363
Get(ordered, "b");  // 39622
Get(ordered, "c");  // 106829

static void Get(IDictionary<string,int> dict, string key) {
    var sw = Stopwatch.StartNew();
    for (long i = 0; i < 100000; ++i) _ = dict[key];
    Console.WriteLine(sw.ElapsedTicks);
}

速度を目的に使うことは必要なさそうですね。

備えよう

備えよう