我需要维护唯一键(由整数键)的数据库对象的缓存。一个查询将一个IEnumerable<MyEntity>实例(MyEntity使用一个int主键)与结果一起提供,并且我想尽快初始化一个Dictionary<int, MyEntity>实例,因为该查询可以返回一些十万行。

Dictionary<int, MyEntity>初始化IEnumerable<MyEntity>实例的最有效方式是什么?

简而言之,我想知道是否有更高效的方法可以做到这一点:

IEnumerable<MyEntity> entities = DoSomeQuery();

var cache = new Dictionary<int, MyEntity>();

foreach (var entity in entities)
    cache.Add(entity.Id, entity);

//or...

cache = entities.ToDictionary(e => e.Id);


当然,查询具有最大的潜在性能后果,但是重要的是,我要为用例尽可能地节省毫秒。

编辑:

值得一提的是,.ToDictionary<TKey, TElement> literally runs a foreach loop与第一个示例类似,因此可以假设性能即使不稍差也将是完全相同的。也许那是我的答案。

最佳答案

您将以最快的速度。

如果您可以快速确定要添加的元素数量,则将其传递为Dictionary构造函数的容量将通过防止内部调整大小操作而有所提升(.cc的.NET Core版本执行该操作) ,其他版本则没有)。

如果按键相对紧凑地包装,那么您可以从调整大小到范围而不是计数中受益。例如。如果您有ToDictionary()Id,那么将大小设置为7(如果缺少的{5, 6, 7, 9, 10, 11}存在,则将具有的值数)而不是6是有益的。(实际上,在这里没有区别,因为效果只会以大于此的设置开始。)但是效果很小,因此如果您要浪费大量内存,则不值得这样做(例如,绝对不值得将8存储在300个容量的字典中!好处是增加了密钥的使用频率)在内部大小(因此减少内部哈希值减少)小于将它们全部添加完毕之后的时间内,将其哈希化为不会与另一个元素冲突的东西。

如果它们紧紧包装,但您无法预测大小,那么按顺序存储它们将有好处,因为随着内部存储的增长,字典中经常会希望存储一些未使用的减少的哈希值码。好处是,它比在内存中排序的成本要小(而且无论如何,无论是显式还是在{8, 307}操作中,都需要查找元素的数量),因此,只有在有一种方法可以完成排序的情况下,它才有用你便宜。 (例如,某些Web服务要求提供某种排序条件,因此您最好提供ID作为条件。通常,这不适用)。

这些点,尤其是最后两个点,影响很小,可能不会增加任何可测量的内容。如果第一个计数不超过使用OrderByCount操作便宜的源,那么即使第一个计数也将小于获取计数的成本。

Length本身可以通过替换为索引来改进(在适用时),但有时情况更糟。它也倾向于在某些具体类型的源上做得更好(即foreach数组上的foreachT[]上的foreachList<T>上的foreach),但这意味着在层之间公开实现细节,并且很少值得,尤其是因为许多收集类型都没有从中受益。

09-30 17:06
查看更多