List <t> FirstOrDefault()性能不佳 - 在这种情况下是否可以使用字典?</t>

时间:2011-01-26 14:24:52

标签: c# linq performance linq-to-sql list

我有一组'代码'Z在某段时间内有效。

由于我在大循环(百万+)中需要它们很多次,每次我必须查找相应的代码时,我将它们缓存在List&lt;&gt;中。找到正确的代码后,我插入(使用SqlBulkCopy)一百万行。

我使用以下代码查找ID(l_zList<T>

var z_fk = (from z in l_z
            where z.CODE == lookupCode &&
                  z.VALIDFROM <= lookupDate &&
                  z.VALIDUNTIL >= lookupDate 
            select z.id).SingleOrDefault();

在其他情况下,我使用了具有卓越性能的词典,但在这些情况下,我只需要根据代码查找id。

但是现在通过搜索字段组合,我陷入困境。

有什么想法吗?提前谢谢。

4 个答案:

答案 0 :(得分:4)

创建一个字典,用于存储每个查找代码的项目列表 - Dictionary<string, List<Code>>(假设查找代码是字符串,对象的类型为代码)。

然后,当您需要根据lookupDate进行查询时,可以直接从dict[lookupCode]运行查询:

var z_fk = (from z in dict[lookupCode]
            where z.VALIDFROM <= lookupDate &&
                  z.VALIDUNTIL >= lookupDate 
            select z.id).SingleOrDefault();

然后只需确保每当有新的Code对象时,它都会被添加到与List<Code>对应的dict中的lookupCode集合中(如果没有存在,然后创建它。)

答案 1 :(得分:4)

一个简单的改进就是使用......

//in initialization somewhere
ILookup<string, T> l_z_lookup = l_z.ToLookup(z=>z.CODE);

//your repeated code:
var z_fk = (from z in lookup[lookupCode]
            where z.VALIDFROM <= lookupDate && z.VALIDUNTIL >= lookupDate 
            select z.id).SingleOrDefault();

您可以进一步使用更复杂,更智能的数据结构,以排序方式存储日期,并使用二进制搜索来查找ID,但这可能就足够了。此外,您谈到SqlBulkCopy - 如果您正在处理数据库,也许您可​​以在数据库上执行查询,然后只需创建适当的索引,包括CODE,VALIDUNTIL和VALIDFROM列。

我通常更喜欢使用Lookup而不是Dictionary来包含Lists,因为构建它并且具有更清晰的API(例如,当密钥不存在时)。

答案 2 :(得分:1)

这对我来说听起来像是一种情况,这种情况可能都是通过一个语句在数据库上发生的。然后,您可以使用索引来快速保持查询,避免必须通过线路将数据推送到数据库或从数据库传输数据。

答案 3 :(得分:1)

我们没有足够的信息来提供非常规范的建议 - 但是您应该考虑一些一般性的事情。

时间值是什么类型?您是在比较日期时间还是某些原始值(如time_t)。考虑一下数据类型如何影响性能。选择最好的。

你真的应该在内存中执行此操作,还是应该将所有这些行放入SQL并让它在那里查询?这真的很擅长。

但是,让我们坚持你所询问的内容 - 在记忆搜索中。

当搜索时间过长时,只有一个解决方案 - 搜索更少的东西。您可以通过以一种允许您使用尽可能少的操作轻松排除尽可能多的节点的方式对数据进行分区来实现此目的。

在您的情况下,您有两个条件 - 代码和日期范围。以下是一些想法......

您可以根据代码进行分区 - 即字典&gt; - 如果你有许多均匀分布的代码,你的列表大小将各自大小为N / M(其中N =总事件数,M =事件数)。因此,拥有10个代码的百万个节点现在需要搜索100k项而不是100万个。但你可以更进一步。列表本身可以通过开始时间进行排序,允许二进制搜索非常快速地排除许多其他节点。 (这当然需要在构建数据收集时进行权衡)。这应该提供非常快速的

您可以根据日期进行分区,只需将所有数据存储在按开始日期排序的单个列表中,然后使用二进制搜索查找开始日期,然后前进以查找代码。这种方法对字典有益处吗?这取决于你的其他程序。也许成为IList很重要。我不知道。你需要明白这一点。

您可以将字典模型分区数据按开始时间四舍五入到某个边界(取决于事件的长度,粒度和频率)。这基本上是将数据存储到具有相似开始时间的组中。例如,在12:00和12:01之间开始的所有事件可能在一个桶中,等等。如果您有非常少量的事件和很多频繁(但不是病态)事件,这可能会给你非常好的查找​​性能。

重点是什么?想想你的数据。考虑添加新数据应该有多昂贵以及查询数据应该有多昂贵。考虑一下您的数据类型如何影响这些特征。根据该数据做出明智的决定。如果有疑问,让SQL为你做。