C#LINQ First()比ToArray()快吗?
我正在进行测试。
看起来像:
方法1)
List = new List{1,2,4, .....} //assume 1000k var result ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).First();
方法2)
List = new List{1,2,4, .....} //assume 1000k var result = ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).ToArray()[0];
为什么方法2与方法1相比是如此之慢?
嗯…因为你正在创建一个额外的数组(而不是只使用迭代器)。 第一种方法在第一次匹配后停止( Where
是非缓冲的流API)。 第二个将所有匹配加载到一个数组中(可能有几个重新resize),然后获取第一个项目。
作为旁注; 你可以创造无限的序列; 第一种方法仍然可行,第二种方法将永远运行(或爆炸)。
它也可能是:
var result ErrorCodes.First(x => ReturnedErrorCodes.Contains(x));
(这不会让它更快,但也许更容易阅读)
你有一个包含一千个硬币的jar子,其中许多是硬币。 你想要一毛钱。 以下是解决问题的两种方法:
-
将硬币从jar子里拉出来,一次一个,直到你得到一角硬币。 现在你有一毛钱。
-
将硬币从jar子里拉出来,一次一个,把硬币放在另一个jar子里。 如果那个jar子太小,可以将它们一次一个地移动到一个更大的jar子里。 继续这样做,直到你在最后一个jar子里有所有硬币。 那个jar子可能太大了。 制造一个足够大的jar子来容纳那么多硬币,然后一次一个地移动硬币到新的jar子里。 现在开始从那个jar子里拿出硬币。 取出第一个。 现在你有一毛钱。
现在清楚为什么方法1比方法2快很多?
因为延期执行。
代码ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x))
不返回整数集合,而是返回一个能够返回整数流的表达式。 在您开始从中读取整数之前,它不会执行任何实际工作。
ToArray
方法将使用整个流并将所有整数放在一个数组中。 这意味着必须将整个列表中的每个项目与错误代码进行比较。
另一方面, First
方法只从流中获取第一个项目,然后停止从流中读取。 这将使它更快,因为它会在找到匹配后立即停止将列表中的项目与错误代码进行比较。
因为ToArray()
将整个序列复制到一个数组。
方法2必须迭代整个序列以构建数组,然后返回第一个元素。
方法1只是遍历足够的序列以找到第一个匹配元素。
ToArray()
遍历它给出的整个序列,并创建和排列它。
如果你没有调用ToArray()
, First()
让Where()
只返回匹配的第一个项并立即返回。
First()是O(1)的复杂性
ToArray()[0]是复杂度O(n)+1
var @e = array.GetEnumerator(); // First @e.MoveNext(); return @e.Current; // ToArray (with yield [0] should as fast as First...) while (@e.MoveNext() { yield return @e.Current; }
因为在第二个示例中,您实际上是将IEnumerable
转换为数组,而在第一个示例中,没有发生转换。
在方法2中,必须首先将整个数组转换为数组。 而且,当First()
更具可读性时,混合数组访问似乎很尴尬。
这是有道理的,ToArray可能涉及一个副本,它总是会更昂贵,因为Linq无法保证你将如何使用你的数组,而First()只能返回单个元素。开始。