上記の回答に追加したかったのは、入れ子になったディクショナリがメモリ フットプリントの点で複合キー ディクショナリよりもはるかに優れているいくつかのシナリオ (データの分散方法によって異なります) があることです (これにより、パフォーマンスが向上する可能性があります)。全体)。その理由は、ネストにより、キーの重複した値を保存する必要がなくなるためです。これにより、大きな辞書では、余分な辞書のフットプリントが無視できるようになります。
たとえば、(男性/女性)、(赤ちゃん/若い/古い)、(年齢) の複合キーを持つ辞書が必要だとします。
複合キーの辞書でいくつかの値を保存しましょう。
(male, baby, 1)
(male, baby, 2)
(male, baby, 3)
(male, young, 21)
(male, young, 22)
(male, young, 23)
(male, old, 91)
(male, old, 92)
(male, old, 93)
(female, baby, 1)
(female, baby, 2)
(female, baby, 3)
(female, young, 21)
(female, young, 22)
(female, young, 23)
(female, old, 91)
(female, old, 92)
(female, old, 93)
次に、辞書の辞書に同じ値を保存しましょう。
male -> baby -> 1
2
3
young -> 21
22
23
old -> 91
92
93
female -> baby ->1
2
3
young -> 21
22
23
old -> 91
92
93
複合キー アプローチでは、辞書の辞書に 1 つのコピーを保存するのではなく、「男性」と「女性」のコピーを 9 回保存します。実際、26 個のアイテムに対して 54 個のアイテムを保存したので、メモリ使用量が 2 倍になりました。この例は、違いを視覚化するのにも役立ちます。最初のサンプルと比較して 2 番目のサンプルにどれだけの「空の」スペースがあるかを確認してください。これらはすべて保存する必要のない値です。
まだ納得していない人のために、ここにサンプルテストがあります:
Dictionary<Tuple<int, int, int>, int> map1 = new Dictionary<Tuple<int, int, int>, int>();
Dictionary<int, Dictionary<int, Dictionary<int, int>>> map2 = new Dictionary<int, Dictionary<int, Dictionary<int, int>>>();
public void SizeTest()
{
for (int x = 0; x < 30; x++)
{
for (int y = 0; y < 100; y++)
{
for (int z = 0; z < 600; z++)
{
addToMap1(x, y, z, 0);
addToMap2(x, y, z, 0);
}
}
}
int size1 = GetObjectSize(map1);
int size2 = GetObjectSize(map2);
Console.WriteLine(size1);
Console.WriteLine(size2);
}
private void addToMap1(int x, int y, int z, int value)
{
map1.Add(new Tuple<int, int, int>(x, y, z), value);
}
private void addToMap2(int x, int y, int z, int value)
{
map2.GetOrAdd(x, _ => new Dictionary<int, Dictionary<int, int>>())
.GetOrAdd(y, _ => new Dictionary<int, int>())
.GetOrAdd(z, _ => value);
}
private int GetObjectSize(object TestObject)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
byte[] Array;
bf.Serialize(ms, TestObject);
Array = ms.ToArray();
return Array.Length;
}
public static TResult GetOrAdd<TKey, TResult>(this Dictionary<TKey, TResult> map, TKey key, Func<TKey, TResult> addIfMissing)
{
TResult result;
if (!map.TryGetValue(key, out result))
{
result = addIfMissing(key);
map[key] = result;
}
return result;
}
このテストは、辞書の辞書を支持して ~30MB に対して ~70MB を返します。