列表中元素的计数<;列表<;T>>;

本文关键字:lt 列表 gt 元素 | 更新日期: 2023-09-27 17:58:13

我有一个List<List<T>>。如何以最快的方式计算其中的所有元素,就好像它是一个单独的List<T>一样?

到目前为止,我已经使用

List<int> result = listOfLists
  .SelectMany(list => list)
  .Distinct()
  .ToList().Count;

但这实际上创建了一个列表,然后对元素进行计数,这不是一个好主意。

列表中元素的计数<;列表<;T>>;

通过使用LINQ,我认为您的代码很好,有一些不需要.ToList()的更改,只需如下调用Count()扩展即可:

int result = listOfLists.SelectMany(list => list).Distinct().Count();

如果您需要消除列表之间的重复,我建议使用一个简单的、带有HashSet的嵌套循环。它将SelectMany和Distinct操作组合到集合插入逻辑中,并且应该更快,因为HashSet有O(1)查找时间。Internal Distinct()实际上可能使用类似的东西,但这完全省略了单个列表的构造。

var set = new HashSet<T>();
foreach (var list in listOfLists)
{
    foreach (var item in list)
    {
        set.Add(item);
    }
}
var result = set.Count;

要计算列表中所有列表中的所有元素,可以使用聚合运算符:

int count = listOfLists.Sum(l => l.Distinct().Count());

我想有机会回答这个问题,只是为了强调我们应该在什么时候使用linq,什么时候使用classic。不幸的是,今天人们不太关心性能,因为我们已经习惯了在功能强大的计算机上工作。不管怎样,只要尝试下面的代码,你就会发现Linq比经典的for版本慢了100多倍。只有当您需要编写的表达式非常复杂并且您希望使其更可读时,才应该使用Linq。我没有花时间在下面的解决方案上,因为我想专注于性能

public static void Main(string [] arg)
{
    //create the list
    List<List<string>> listOfList = new List<List<string>>()
                                      {
                                          new List<string>()
                                              {
                                                  "1.1","2.2"
                                              }
                                      ,
                                       new List<string>()
                                              {
                                                  "2.1","2.2","2.3"
                                              }
                                      };
    //stopwatch using Linq
    Stopwatch stopwatch=new Stopwatch();
    stopwatch.Start();
    int totalUsingLinq = listOfList.Sum(x => x.Count);
    stopwatch.Stop();
    Console.WriteLine("Using Linq:{0}",stopwatch.Elapsed); //00005713
    int totalUsingFor = 0;
    //stopwatch using classic for 
    stopwatch.Reset();
    stopwatch.Start();
    totalUsingFor = 0;
    for(int i=0;i<listOfList.Count;i++)
    {
       var mainItem = listOfList[i];
        if(mainItem!=null)
        {
            totalUsingFor += mainItem.Count;
        }
    }
    stopwatch.Stop();
    Console.WriteLine("Using for:{0}", stopwatch.Elapsed); //0000010
}

使用for的不同版本(仅作为示例)。在这种情况下,我创建了一个非常"瓶颈"的函数,它可以做不同的事情,而且速度更快。

 public class Program
    {
      public static void Main(string[] arg)
        {
            //create the list
            List<List<string>> listOfList = new List<List<string>>()
                                      {
                                          new List<string>()
                                              {
                                                  "1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1"
                                              }
                                      ,
                                       new List<string>()
                                              {
                                                  "2.1","2.2","2.3","2.3","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1"
                                              }
                                      };
            //stopwatch using Linq
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            int totalUsingLinq = listOfList.Sum(l => l.Distinct().Count());

            stopwatch.Stop();
            Console.WriteLine("Using Linq:{0}", stopwatch.Elapsed); //000012150    
            int totalUsingFor = 0;
            //stopwatch using classic for 
            stopwatch.Reset();
            stopwatch.Start();
            totalUsingFor = 0;
            for (int i = 0; i < listOfList.Count; i++)
            {
                var mainItem = listOfList[i];
                if (mainItem != null)
                {
                    for(int y=0;y<mainItem.Count;y++)
                    {
                      if(mainItem[y]!=null)
                      {
                          totalUsingFor++;
                          NullDuplicateItems(y, ref mainItem);
                      }   
                    }
                }
            }
            stopwatch.Stop();
            Console.WriteLine("Using for:{0}", stopwatch.Elapsed); //0009440
        }
        public static void NullDuplicateItems(int index,ref List<string > list)
        {
            var item = list[index];
            for(int i=index+1;i<list.Count;i++)
            {
                if(list[i]==item)
                {
                    list[i] = null;
                }
            }
        }
    }