我有2 IEnumerable<int>
IEnumerable<int> x;
IEnumerable<int> y;
确定x中是否存在任何int的最佳方法是什么? 目前我正在使用:
return x.Intersect<int>(y).Count() > 0;
循环并单独测试它们会快得多吗?
foreach (int i in x)
{
foreach (int j in y)
{
if (i == j) return true;
}
}
return false;
列表相对较轻,x中不超过50个,如果考虑重要则为4个y。
答案 0 :(得分:27)
return x.Intersect<int>(y).Any();
这假设IEnumerable<int>
实现也没有实现ICollection<int>
。在这种情况下,Count
(在IEnumerable<T>
实现ICollection<T>
的情况下)是O(N)操作,而Any
总是一个O (1)操作。 (因为它只检查单个元素)。但是,Count
的行为是一个实现细节,您不应该依赖它。
我已经更深入地了解了这个in a blog post,详细说明了何时使用Count()
与Any()
。总结:
Enumerable.Any
扩展方法检查序列中是否存在元素。Enumerable.Count
扩展方法进行与零的比较,因为以下内容在语义上是等效的:
sequence.Count() == 0
!sequence.Any()
Enumerable.Count
扩展方法与“非零”条件进行比较,因为以下语义等效:
sequence.Count != 0
sequence.Any()
答案 1 :(得分:4)
GetNext()
进行的所有调用,构建HashSet
等实际上都会比使用Intersect(y).Any()
更加昂贵。但是,在这种情况下,无论如何这两个电话都非常快。
换句话说,Intersect(y).Any()
肯定会随着两个序列变长而变得更好,但是如果你绝对确定序列很短,那么嵌套循环解决方案会更快
原始回答
不,Intersect()
将比双循环解决方案更快 - 但正如CasperOne所写,Any()
将比Count()
更快,因为它会在看到元素时立即退出
假设长度为N和M的序列,相交将为O(N + M)
,而双循环解为O(N * M)
。
Intersect从“内部”序列构建HashSet
(这需要O(M)
复杂度),然后遍历外部序列(需要O(N)
复杂度),产生任何元素在集合中。这些结果是流式传输的 - 当从Intersect()
请求第一个元素时,将评估内部序列,但这只能找到第一个匹配(如果有的话)。使用Any()
如果有任何匹配,您将有一个“早出”,因此在计算复杂性时我们不需要考虑整体匹配数。
来自LINQ岩石的流式传输结果 - 非常值得您全面了解(以及延迟执行)。
答案 2 :(得分:3)
Intersect
没问题,但正如其他人所说,我不会在结果上拨打.Count()
。
原因是Intersect 不创建两个列表的交集。它会创建一个能够 枚举该交集的IEnumerable
,但它实际上并没有枚举这些结果。大多数工作都推迟到最后迭代此枚举的时间。
Count
的问题在于它会迭代整个枚举。因此,它不仅总是计算所有结果,而且还会导致计算这些结果所涉及的所有工作也运行。
通过比较,快速调用Any
将非常,因为在返回之前,您将最多计算 一个交叉点结果。当然,在没有匹配的情况下,仍然需要迭代整个列表。然而,这并不比以前更糟糕。事实上,它仍然更快,因为正如Jon Skeet所提到的,Intersect
函数使用HashSet来计算结果而不是迭代所有内容。您的最佳和平均病例都得到了极大的改善。
这就像这两个片段之间的区别:
int count = 0;
foreach (int i in x)
{
foreach (int j in y)
{
if (i==j) count++;
}
}
return (count > 0);
// this one should look familiar
foreach (int i in x)
{
foreach (int j in y)
{
if (i==j) return true;
}
}
return false;
显然,第二名的平均速度要快得多。 Any()
的性能与类似(与HashSet不同)第二个片段,而Count()
与第一个类似。
答案 3 :(得分:1)
你最好这样做:
return x.Intersect(y).Any();
一旦找到一个匹配项就会中止,并停止枚举这些集合。
答案 4 :(得分:0)
这个问题和最后一个答案都超过了1年,因为我的回答;但是,我的发现与接受的答案不同。我发现循环比使用Intersect.Any()快得多。也许我的基准代码不对?
这是我用来查找Intersect,嵌套循环和带IndexOf的循环之间每秒迭代次数的代码。请原谅VB。 ;)
Dim ArrayLength As Integer = 50
Dim doesContain As Boolean = False
Dim counter As Integer = 0
Dim sw As New System.Diagnostics.Stopwatch()
Dim BigArray1 As String() = New String(ArrayLength) {}
Dim BigArray2 As String() = New String(ArrayLength) {}
Dim rand As New Random(DateTime.Now.Millisecond)
For i As Integer = 0 To ArrayLength
BigArray1(i) = Convert.ToChar(rand.Next(0, 255)).ToString()
BigArray2(i) = Convert.ToChar(rand.Next(0, 255)).ToString()
Next
Dim AnEnumerable1 As IEnumerable(Of String) = BigArray1
Dim AnEnumerable2 As IEnumerable(Of String) = BigArray2
counter = 0
sw.Restart()
Do
doesContain = False
For Each x As String In AnEnumerable1
For Each y As String In AnEnumerable2
If x.Equals(y) Then
doesContain = True
Exit For
End If
Next
If doesContain Then Exit For
Next
counter += 1
Loop While sw.ElapsedMilliseconds < 1000
Console.WriteLine("InnerLoop iterated: " & counter.ToString() & " times in " & sw.ElapsedMilliseconds.ToString() & "ms.")
counter = 0
sw.Restart()
Do
doesContain = AnEnumerable1.Intersect(AnEnumerable2).Any()
counter += 1
Loop While sw.ElapsedMilliseconds < 1000
Console.WriteLine("Intersect iterated: " & counter.ToString() & " times in " & sw.ElapsedMilliseconds.ToString() & "ms.")
counter = 0
sw.Restart()
Do
For Each x As String In AnEnumerable1
If Array.IndexOf(Of String)(BigArray2, x) <> -1 Then
Exit For
End If
Next
counter += 1
Loop While sw.ElapsedMilliseconds < 1000
Console.WriteLine("IndexOf iterated: " & counter.ToString() & " times in " & sw.ElapsedMilliseconds.ToString() & "ms.")
我在两个5,000,000个项目数组中的结果。更高的迭代次数更好:
我的结果超过两个50个项目数组。更高的迭代次数更好: