我需要维护唯一键(由整数键)的数据库对象的缓存。一个查询将一个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作为条件。通常,这不适用)。
这些点,尤其是最后两个点,影响很小,可能不会增加任何可测量的内容。如果第一个计数不超过使用OrderBy
或Count
操作便宜的源,那么即使第一个计数也将小于获取计数的成本。Length
本身可以通过替换为索引来改进(在适用时),但有时情况更糟。它也倾向于在某些具体类型的源上做得更好(即foreach
数组上的foreach
在T[]
上的foreach
在List<T>
上的foreach
),但这意味着在层之间公开实现细节,并且很少值得,尤其是因为许多收集类型都没有从中受益。