【问题标题】:Improve performance of List<T>提高 List<T> 的性能
【发布时间】:2016-09-02 03:33:38
【问题描述】:

我有一个名为 Product 的类,它包含一些属性,如 Id(作为 Guid)和 Messages(作为 List),还有一个 Message 类,它还包含 Id 和其他属性。我在消息表中有所有消息,在产品表中有所有产品。在获得两个表的数据后,我想加入他们关于 Id 属性的信息。如果我使用下面的代码,因为它是线性搜索,那么性能很糟糕。

foreach (Product product in products)
    product.Messages = messages.Where(n => n.Id == product.Id).ToList();

还有其他更快的方法吗?

谢谢

【问题讨论】:

  • 加入数据库端?如果您绝对需要它在客户端工作,我会使用字典。
  • 为什么不在数据库端执行连接?这将比提取所有数据并在 C# 中执行要快一个数量级。
  • 如果您使用的是实体框架,那么您不应该这样做
  • 顺便问下好问题,引发了很多有趣的讨论。
  • 我需要在客户端拥有所有记录。所以,更喜欢在 C# 中加入他们。无论如何,ToLookup 解决了我的问题。

标签: c#


【解决方案1】:

您可以通过将消息分组到查找表中来加快速度。

messagesDict = messages
    .GroupBy(x => x.Id)
    .ToDictionary(x => x.Id, x.ToList());

或者,正如 John Bustos 所建议的,您可以使用 ToLookup();

messagesDict = messages
    .ToLookup(x => x.Id);

你是这样用的

//you might have to first check if messagesDict 
//actually has any messages for your project.
product.Messages = messagesDict[product.Id];

您最初的尝试是 O(nm),其中 n 是项目数,m 是消息数。

A Dictionary 使用散列,因此从实用的角度来看,您通常可以假设它具有接近 O(1) 的插入,并且 O (1) 搜索。 Under ideal circumstancesList&lt;T&gt;.Add 也是 O(1)。这意味着,如果您要手动创建查找字典,则可以在 O(m) 中完成。我希望像ToLookup 这样的内置函数能够达到同样的效率。

一旦你这样做了,你的算法就会变成 O(n + m)

【讨论】:

  • 啊,你打败了我:(
  • @Sam 我犯了一个错误。 ID 不是整数。它是吉德。那么,您的解决方案是否仍然有效?
  • 分组不是排序,是吗?我们是否知道 Group() 方法内部是否使用了排序算法?
  • @Babi 其中Id 不是int?只要它们都是同一类型,就应该不是问题
  • @Sam 是的,它们是一样的。我要试试看:)
【解决方案2】:

您应该在数据库中进行联接。这将产生最佳性能。如果您坚持在 C# 中按 Id 对产品进行排序,然后按 ID 对消息进行排序。

【讨论】:

  • 我同意在数据库中这样做,因为您可以在 O(log n) 时间内搜索 B-Tree。但是,在我看来,在 C# 中首先按 ID 排序实际上会更糟;线性搜索是 O(n),但大多数排序算法是 O(n log n) 或 O(n^2),请参阅 bigocheatsheet.com
  • @EJoshuaS 除非您在按分支预测排序后获得更大的收益。见stackoverflow.com/questions/11227809/…
  • @EJoshuaS,这是假设产品是一对多带有消息的,虽然很可能,但问题中根本没有说明。正如 Thomas 提到的,分支预测。
  • @ThomasWeller 这是一个公平的观点;您链接到的那篇文章很好地解释了这个问题。一方面,例如,如果您正在执行 O(n^2) 操作,则整个算法不可能比 O(n^2) 更好,但是查看该帖子我可以看到您如何才能真正节省总体时间(即使在计算复杂度方面您不能比排序算法做得更好)。
  • 我想,根据排序列表的特征,您也许可以进行事实上的二分搜索。让我想起了我在一次采访中遇到的一个问题:假设您有一个排序数组,其中包含世界上每个人的年龄(因此,例如,如果您有 1,000,000 个 1 岁的人,您将有 1,000,000 个连续的 1在数组中)。你如何计算一个年龄段的人数?为简化起见,我的回答是,排序允许您执行二进制搜索变体来查找该数字的第一个和最后一个实例的索引,然后将它们减去以计数。
【解决方案3】:

正如其他人所指出的,在数据​​库中进行连接。正如你所指出的,线性搜索是 O(n) (在这种情况下你实际上做了很多线性搜索);但是,大多数数据库使用 B-Tree 数据结构(或类似的结构)按主键对行进行排序。这意味着对主键的数据库搜索是 O(log n),这显然 显着 更快。 (当然,假设 Id 是主键)。

【讨论】:

  • 我需要在客户端拥有所有记录。所以,更喜欢在 C# 中加入他们。无论如何,ToLookup 解决了我的问题。谢谢:)
猜你喜欢
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多