确定IEnumerable <t>是否包含另一个IEnumerable的任何对象<t> </t> </t>

时间:2009-03-27 19:19:54

标签: .net ienumerable

我有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。

5 个答案:

答案 0 :(得分:27)

使用Any method代替Count method

最快
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()。总结:

  • DO 使用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个项目数组中的结果。更高的迭代次数更好:

  • InnerLoop迭代:在1000毫秒内完成2974553次。
  • 交叉迭代:1168毫秒中的4次。
  • IndexOf迭代:在1000毫秒内4194423次。

我的结果超过两个50个项目数组。更高的迭代次数更好:

  • InnerLoop迭代:1000分钟内375712次。
  • 相交迭代:在1000毫秒内完成203721次。
  • IndexOf迭代:在1000毫秒内668421次。