【问题标题】:Custom intersect in lambdalambda中的自定义相交
【发布时间】:2013-11-04 05:09:12
【问题描述】:

我想知道这是否可以使用 lambda 表达式来解决:

List<Foo> listOne = service.GetListOne();
List<Foo> listTwo = service.GetListTwo();
List<Foo> result = new List<Foo>();

foreach(var one in listOne)
{
    foreach(var two in listTwo)
    {
        if((one.Id == two.Id) && one.someKey != two.someKey)
           result.Add(one);
    }
}

【问题讨论】:

    标签: c# asp.net linq lambda set-intersection


    【解决方案1】:
    var result = from one in listOne
                 join two in listTwo on one.Id equals two.Id
                 where one.SomeKey != two.SomeKey
                 select one;
    

    更新:似乎有些人认为这没有回答问题,因为它应该没有使用 lambdas。当然可以,只是使用了友好的查询语法。

    这是完全相同的代码,只是可读性较差:

    var result = 
        listOne.Join(listTwo, one => one.Id, two => two.Id, (one, two) => new { one, two })
               .Where(p => p.one.someKey != p.two.someKey)
               .Select(p => p.one);
    

    【讨论】:

      【解决方案2】:

      你可以试试:

      var result = listOne.Join(listTwo,
          (one) => one,
          (two) => two,
          (one, two) => one,
          new MyFooComparer());
      

      MyFooComparer 可能的样子:

      class MyFooComparer : IEqualityComparer<Foo>
      {
          public bool Equals(Foo x, Foo y)
          {
              return x.Id == y.Id && x.someKey != y.someKey;
          }
      
          public int GetHashCode(Foo obj)
          {
              return obj.Id.GetHashCode();
          }
      }
      

      [更新]

      我很好奇 IntersectJoin 的性能,所以我在我的解决方案和 @pswg 的解决方案之间做了一个小的性能比较(listOnelistTwo 各有 10 个项目):

      var comparer = new MyFooComparer();
      var sw = new Stopwatch();
      sw.Start();
      for (int i = 0; i < 100000; i++)
      {
          var result1 = listOne.Intersect(listTwo, comparer).ToList();
      }
      Console.WriteLine("Intersect: {0}",sw.Elapsed);
      sw.Restart();
      for (int i = 0; i < 100000; i++)
      {
          var result = listOne.Join(listTwo,
              (one) => one,
              (two) => two,
              (one, two) => one,
              comparer);
      }
      Console.WriteLine("Join:      {0}", sw.Elapsed);
      

      输出:

      Intersect: 00:00:00.1441810
      Join:      00:00:00.0037063
      

      【讨论】:

      • .net 会以某种方式调整代码。最好以不同的方式运行这些测试并预热一段时间。这不是基准测试的方法
      • @Anirudh,我完全同意,这不是测试它的最佳方式。但即使颠倒顺序,结果也是一样的。
      【解决方案3】:

      当然可以!您可以使用 Linq 的 Intersect 扩展方法的 this overload,它采用 IEqualityComparer&lt;T&gt;,如下所示:

      public class FooComparer : IEqualityComparer<Foo> 
      {
          public bool Equals(Foo x, Foo y)
          {
              return x.Id == y.Id && x.someKey != y.someKey;
          }
      
          public int GetHashCode(Foo x)
          {
              return x.Id.GetHashCode();
          }
      }
      
      ...
      
      var comparer = new FooComparer();
      List<Foo> listOne = service.GetListOne();
      List<Foo> listTwo = service.GetListTwo();
      List<Foo> result = listOne.Intersect(listTwo, comparer).ToList();
      

      【讨论】:

      • 很好,谢谢!如果我与列表 2 相交,反之亦然,这并不重要,对吧?
      • @Johan 我相信可能存在一些性能差异,具体取决于哪个集合更大。当然,如果您以不完全对称的方式实现 IEqualityComparer,那么它会有所作为。
      • Sooo... 从最大的开始以确保安全?
      • @Johan 我不记得有关性能调整的细节,所以如果这是一个性能关键的应用程序,您可能需要先运行一些测试。
      • 关于 intersect 中的第二个参数:我是否必须将它放在实现 IEqualityComparer 的单独类中?
      【解决方案4】:
      listOne.SelectMany(x=>listTwo.Where(y=>x.Id==y.Id && x.someKey != y.someKey));
      

      【讨论】:

      • 看来Johan 很在意性能。虽然这是对他的初始代码的直接翻译,但它也具有相同的 O(n x m) 复杂度。这是迄今为止给出的所有答案中效率最低的。
      • 为什么不呢?使用加入我们的解决方案比他的原始代码高效得多。虽然他可以编写比 Linq 解决方案更高效的代码,但这可能会花费他很长时间,并导致代码不可读、不可维护。
      • @KrisVandermotten 的问题是solve using a lambda expression..OP 从来没有提到任何关于性能的事情..!
      • 我没有说你的回答不好。我只是为 OP 和其他读者的利益添加了信息。我说“似乎是这样”,因为他询问了其他答案的 cmets 中的表现。
      • .SelectMany 正在执行嵌套循环。 .Join 正在执行哈希查找。我建议你在大名单上对这两个答案进行基准测试。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-17
      • 2013-04-14
      • 2021-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多