【问题标题】:find common items across multiple lists in C#在 C# 中跨多个列表查找公共项
【发布时间】:2011-06-29 11:36:52
【问题描述】:

我有两个通用列表:

List<string> TestList1 = new List<string>();
List<string> TestList2 = new List<string>();
TestList1.Add("1");
TestList1.Add("2");
TestList1.Add("3");
TestList2.Add("3");
TestList2.Add("4");
TestList2.Add("5");

在这些列表中查找常见项目的最快方法是什么?

【问题讨论】:

  • @Erno 我看到了那个帖子,但我无法使用代码。
  • 实际上这是一个很好的问题,是编译器错误还是您无法将其适应您的代码?可能很重要,因为您没有指定您使用的 .NET 版本,我们似乎都认为您使用的是最新版本。
  • @Adam Houldsworth 我正在使用 .NET 4。@Erno 这个问题是关于 List> optionLists;我的问题是关于 List 在那个问题中没有人使用过 Intersect 方法。

标签: c# generics


【解决方案1】:

假设您使用具有 LINQ 的 .Net 版本,您可以使用 Intersect 扩展方法:

var CommonList = TestList1.Intersect(TestList2)

【讨论】:

  • 问题是针对多个列表。尽管人们会不断将新列表与先前相交的结果进行比较以达到相同的效果。
【解决方案2】:

如果您有对象列表并想获取某些属性的公共对象,请使用;

var commons = TestList1.Select(s1 => s1.SomeProperty).ToList().Intersect(TestList2.Select(s2 => s2.SomeProperty).ToList()).ToList();

注意: SomeProperty 是指您要实施的一些标准。

【讨论】:

    【解决方案3】:

    假设您有可用的 LINQ。我不知道它是否是最快的,但一个干净的方式是这样的: 罢工>

    var distinctStrings = TestList1.Union(TestList2).Distinct();
    

    var distinctStrings = TestList1.Union(TestList2);
    

    更新:别介意我的回答,我也刚刚了解了相交!

    根据 cmets 中的更新,Union 应用了 distinct,现在我想这很有意义。

    【讨论】:

    • UnionIntersect 已经包含一个隐含的Distinct
    • 哇,很有趣,所以你不需要在最后显式调用 distinct。我会修改我的答案。
    • 值得注意的是,Union 将包含两个列表中的所有项目,而 Intersect 仅获取出现在两个列表中的项目。
    【解决方案4】:

    对两个数组进行排序,从两者的顶部开始,比较是否相等。


    使用散列更快:将第一个数组放入散列中,然后比较第二个数组中的每一项(如果它已经在散列中)。

    我不知道那些 Intersect 和 Union 是否已实现。如果您关心性能,请尝试找出它们的运行时间。当然,如果您需要干净的代码,它们更适合。

    【讨论】:

    • Intersect,UnionDistinct 使用 HashSet&lt;T&gt;
    【解决方案5】:

    按照@logicnp 计算包含每个成员的列表数量,一旦您有了列表列表,这几乎是一行代码:

    List<int> l1, l2, l3, cmn;
    List<List<int>> all;
    
    l1 = new List<int>() { 1, 2, 3, 4, 5 };
    l2 = new List<int>() { 1, 2, 3, 4 };
    l3 = new List<int>() { 1, 2, 3 };
    all = new List<List<int>>() { l1, l2, l3 };
    
    cmn = all.SelectMany(x => x).Distinct()
          .Where(x => all .Select(y => (y.Contains(x) ? 1 : 0))
          .Sum() == all.Count).ToList();
    

    或者,如果您愿意:

    public static List<T> FindCommon<T>(IEnumerable<List<T>> Lists)
    {
      return Lists.SelectMany(x => x).Distinct()
          .Where(x => Lists.Select(y => (y.Contains(x) ? 1 : 0))
          .Sum() == Lists.Count()).ToList();
    }
    

    【讨论】:

      【解决方案6】:

      您可以通过计算所有列表中所有项目的出现次数来做到这一点 - 出现次数等于列表数量的项目对所有列表都是通用的:

          static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
          {
              Dictionary<T, int> map = new Dictionary<T, int>();
              int listCount = 0; // number of lists
      
              foreach (IEnumerable<T> list in lists)
              {
                  listCount++;
                  foreach (T item in list)
                  {
                      // Item encountered, increment count
                      int currCount;
                      if (!map.TryGetValue(item, out currCount))
                          currCount = 0;
      
                      currCount++;
                      map[item] = currCount;
                  }
              }
      
              List<T> result= new List<T>();
              foreach (KeyValuePair<T,int> kvp in map)
              {
                  // Items whose occurrence count is equal to the number of lists are common to all the lists
                  if (kvp.Value == listCount)
                      result.Add(kvp.Key);
              }
      
              return result;
          }
      

      【讨论】:

      • 此功能是否与var Common = List1.Intersect(List2).Intersect(List3); 相同,否则 Intersect 可能会因不同时检查所有列表而中断,就像它会跳过其中任何一个一样?我在知道 Intersect var Common = Common.RemoveAll(i =&gt; !List1.Contains(i)); Common.RemoveAll(i =&gt; !List2.Contains(i)); = Common.RemoveAll(i =&gt; !List3.Contains(i)); Common 之前使用过,首先使用 AddRange() 由单个列表填充,然后对每个列表重复,我也无法破坏它,但我有一种直觉它会破坏..
      【解决方案7】:

      使用 HashSet 进行快速查找。这是解决方案:

      using System;
      using System.Linq;
      using System.Collections.Generic;
      
      public class Program
      {
          public static void Main()
          {
              List<int> list1 = new List<int> {1, 2, 3, 4, 5, 6 };
              List<int> list2 = new List<int> {1, 2, 3 };
              List<int> list3 = new List<int> {1, 2 };
      
              var lists = new IEnumerable<int>[] {list1, list2, list3 };
      
              var commons = GetCommonItems(lists);
              Console.WriteLine("Common integers:");
              foreach (var c in commons)
                  Console.WriteLine(c);
      
          }
      
          static IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
          {
              HashSet<T> hs = new HashSet<T>(lists.First());
              for (int i = 1; i < lists.Length; i++)
                  hs.IntersectWith(lists[i]);
              return hs;
          }
      }
      

      【讨论】:

        【解决方案8】:

        使用Intersect 方法:

        IEnumerable<string> result = TestList1.Intersect(TestList2);
        

        【讨论】:

        • 谢谢,但为什么是 Ienumerable ?我认为它应该是 Ienumerable.
        • @shaahin - 是的 - 简单的错字。
        • 或者更好的是,从 C# 3.0 开始,使用 var 并让编译器保持直接。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-08
        • 1970-01-01
        • 2013-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多