为啥在将 First() 应用于投影时,此 linq 代码会以指数方式变慢?
Posted
技术标签:
【中文标题】为啥在将 First() 应用于投影时,此 linq 代码会以指数方式变慢?【英文标题】:why does this linq code get exponentially slower when applying First() to projection?为什么在将 First() 应用于投影时,此 linq 代码会以指数方式变慢? 【发布时间】:2011-03-01 03:55:59 【问题描述】:我正在尝试解析一个 500K 的文本文件。
这更像是一种学习练习 - 我知道还有其他方法可以得到我的结果。
我可能会错误地使用 linq-go,因为我对它还是有点陌生。
我的电脑很快。
我确定我在这里犯了“经典错误”之一 - 所以我的问题是:它是哪一个,我可以纠正我的逻辑还是这不适合 Linq?
var lines = File.ReadAllLines(@"C:\Users\aanodide\Desktop\APIUserGuide.txt");
// add line numbers
var qa = lines
.Select((c,i) => new
i = i,
c = c
);
var qb = qa.Skip(2312); // defs start at > 2312
var qc = qb.Where( c => Regex.IsMatch(c.c, @"(\w+): ([a-zA-Z])?(.*)") );
var qd = qc.Where( c => c.c.StartsWith("API Name:") );
var qd_desc = qc.Where( c => c.c.StartsWith("Description:") ).Select( d => d.i );
var qe = qd.Select( c => new
i = c.i,
c = c.c,
d = qd_desc.First(e => e > c.i) // --> IF I COMMENT OUT THIS, IT RUNS FAST, IN A FRACTION OF A SECOND<--
);
// Take(1) -> .013s
// Take(10) -> .070s
// Take(20) -> .446s
// Take(40) -> 1.63s
// Take(80) -> 6.49s
foreach (var element in qe.Take(50))
Console.WriteLine (element.i);
【问题讨论】:
如果我能教大家一件事关于 LINQ,那就是:查询的值是 一个查询。不是查询结果的值!查询的值是查询本身。在您运行查询之前,它不会为您提供结果,并且每次 运行查询时,查询都会运行。如果您不希望查询多次运行请不要多次运行查询。 有什么 linq 可以让您绘制执行路径的图表吗?我的直觉是,这样的工具可以帮助我自己解决这个问题。 【参考方案1】:正如 Mark 所说,当您调用 First()
时,整个查询都会被迭代。并且First()
对qd
中的每一项都调用一次——这意味着整个文件对qd
中的每一项都解析一次。
要修复它,您可以ToList()
qd_desc
,然后在该列表中执行First()
。那么它只会被评估一次。
即
var qd_desc = qc.Where( c => c.c.StartsWith("Description:") ).Select( d => d.i ).ToList();
【讨论】:
【参考方案2】:当您调用First()
时,将执行整个查询(即迭代序列)。当您调用Skip()
、Where()
、Select()
或任何返回IEnumerable
的运算符时,查询将不会执行,直到您遍历序列。它发生在First()
(或任何返回单个项目的运算符)上的原因是您当时需要一个特定的项目,因此查询必须运行以产生该结果。
【讨论】:
如果我所做的只是用 first 注释掉该行,那么序列的迭代将在几分之一秒内完成...... 正如@saus 指出的那样,您正在为qd
中的每个项目调用First()
。以上是关于为啥在将 First() 应用于投影时,此 linq 代码会以指数方式变慢?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 PhoneGap Android 应用程序在将一堆数据插入 SQL 时崩溃?
为啥在将 Spring Data JPA 与 Spring Boot 结合使用时,我的数据库自定义没有得到应用?