将IEnumerable转换为字典以获得性能?

时间:2011-09-07 08:20:17

标签: c# .net dictionary

我最近在我的公司看到了一个新趋势,我们通过简单的LINQ转换将IEnumerable更改为字典,如下所示:

enumerable.ToDictionary(x=>x);

当集合上的操作是包含/访问时,我们大多数情况下都会这样做,显然字典在这种情况下具有更好的性能。

但是我意识到将Enumerable转换为字典有其自身的成本,我想知道它在什么时候开始收支平衡(如果确实如此),即 IEnumerable的性能包含/访问权限等于ToDictionary +访问/包含。

好的我可能会补充说没有数据库访问可以从数据库查询中创建枚举,就是这样,可以在之后编辑枚举...

知道密钥的数据类型如何影响性能会很有趣吗?

查询可能一般是2-5次,但有时也可能是一次。但我见过像这样的东西 对于可枚举的:

 var element=Enumerable.SingleorDefault(x=>x.Id);
 //do something if element is null or return

获取字典:

 if(dictionary.ContainsKey(x))
 //do something if false else  return

这已经困扰了我很长一段时间了。

5 个答案:

答案 0 :(得分:7)

字典与IEnumerable相比的性能

正确使用时,Dictionary总是更快读取(除非数据集非常小,例如10项)。创建它时可能会有开销。

给定m作为针对同一对象执行的查找量(这些是近似值):

  • IEnumerable的性能(从干净列表中创建):O(mn)
    • 这是因为您需要每次查看所有项目(主要是m * O(n))。
  • Dictionary的效果:O(n) + O(1m)O(m + n)
    • 这是因为您需要先插入项目(O(n))。

通常可以看出Dictionarym > 1时获胜,而IEnumerablem = 1m = 0时获胜。

一般来说,你应该:

  • 针对同一数据集多次执行查找时使用Dictionary
  • 在执行查找时使用IEnumerable
  • 当数据集太大而无法放入内存时,请使用IEnumerable
    • 请记住,SQL表可以像Dictionary一样使用,因此您可以使用它来抵消内存压力。

进一步考虑

Dictionary使用GetHashCode()来组织其内部状态。 Dictionary的性能以两种方式与哈希码密切相关。

  • 效果不佳GetHashCode() - 每次添加,查找或删除项目时都会产生开销。
  • 低质量哈希码 - 导致词典没有O(1)查找性能。

大多数内置.Net类型(尤其是值类型)都有非常好的散列算法。但是,类似列表的类型(例如字符串)GetHashCode()具有O(n)性能 - 因为它需要遍历整个字符串。因此,字典的性能可以被视为(M对于高效GetHashCode()来说是最好的哦:O(1) + M

答案 1 :(得分:2)

这取决于....

IEnumerable有多长?

访问IEnumerable会导致数据库访问吗?

多久访问一次?

最好的办法是进行实验和剖析。

答案 2 :(得分:1)

如果您经常通过某个键搜索集合中的元素 - 定义字典会更快,因为或者基于散列的集合和搜索时间更快,否则如果您不通过集合搜索很多 - 转换不是必需的,因为转换的时间可能比您在集合中的一两次搜索更大,

答案 3 :(得分:0)

恕我直言:您需要使用具有代表性的数据在您的环境中进行测量。在这种情况下,我只需编写一个快速控制台应用程序来衡量代码执行的时间。为了获得更好的衡量标准,我需要多次执行相同的代码。

ADD:

它还取决于您开发的应用程序。通常,您会在这个时间和精力上获得更多优化其他地方(避免网络环境,缓存等)。

答案 4 :(得分:0)

我要补充一点,你没有告诉我们每次你“回放”IEnumerable<>时会发生什么。它是否直接由数据收集支持? (例如List<>)还是“动态”计算?如果它是第一个,对于小集合,枚举它们以找到想要的元素更快(3/4元素的字典是没用的。如果你想我可以建立一些基准来找到断点)。如果它是第二个,那么你必须考虑是否“缓存”集合中的IEnumerable<>是个好主意。如果是,那么您可以选择List<>Dictionary<>,然后返回到第1点。IEnumerable是小还是大?还有第三个问题:如果集合没有备份,但它对于内存来说太大了,那么显然你不能把它放在Dictionary<>中。那么也许是时候让SQL为你工作了: - )

我会补充说“失败”会产生费用:如果你试图找到一个不存在的元素,则在List<>中,费用为O(n),而在{{1}费用仍为Dictionary<>