5

这个问题与这个有关,但我认为并不完全相同。

鉴于:

class Foo
{
  public string Bar { get; set; }
}
...
var c1 = new List<Foo>() { ... };
var c2 = new List<Foo>() { ... };

以下 2 个循环给出相同的结果:

  foreach (var item in c2.Where(f => c1.Any(f1 => f1.Bar.Equals(f.Bar))))
  { ... }

  foreach (var item in c2.Where(f => c1.Select(f1 => f1.Bar).Contains(f.Bar)))
  { ... }

它们同样快吗?

与另一个问题的不同之处在于,这里的额外语句是否Select改变了基础集合性质的重要性。

换句话说:这是否包含:

foos.Contains(foo1)

对与此相同的“集合”采取行动:

foos.Select(f=>f.Bar).Contains(foo1.Bar)

我可能的 - 天真 - 想法可能是:“一旦我们落后于 Linq 的 Select,一切都只是'列表',所以 Any 和 Contains 都是 O(n)。”

4

2 回答 2

18

这两个查询从根本上实现了相同的算法。c1它们将分别对 中的每个项目进行迭代c2,比较Bar两个对象的属性,并在找到匹配项后立即返回。这两种情况的渐近复杂度是相同的,这意味着随着两组规模的增加,它们的扩展性都一样好(或者同样糟糕,视情况而定)。

与一种方法相关的开销与另一种方法相关的开销可能在两者之间存在微小差异,但差异不会很大,并且随着集合大小的增加,它们会越来越小。没有任何真正的性能理由来选择两者中的一个。

有一个您没有显示的选项比其中任何一个都快得多。您可以使用 aJoin来查找c1其中也存在的所有项目,c2而无需对序列进行线性搜索:

var query = from first in c1
    join second in c2
    on first.Bar equals second.Bar
    select first;

另一种选择是使用 aHashSet而不是 a List,因为这样可以更容易地搜索:

var set = new HashSet<string>(c1.Select(item => item.Bar));

var query = c2.Where(item => set.Contains(item.Bar));

(这个解决方案非常接近Join内部的做法。)

这两种解决方案都比您提出的任何一种解决方案都要快得多

于 2013-06-25T16:24:23.353 回答
0

您的第一种方法将迭代和比较一次并返回结果。

第二个查询会更慢,因为它将迭代并将Bar属性提取到集合中,然后迭代并比较f.Bar以创建最终结果。

于 2013-06-25T16:09:41.440 回答