Количество элементов в списке <Список <T>>
У меня есть List<List<T>>
.
Как я могу считать все элементы в этом, как если бы это был один List<T>
самым быстрым способом?
До сих пор я использовал
List<int> result = listOfLists
.SelectMany(list => list)
.Distinct()
.ToList().Count;
но это фактически создает список, а затем подсчитывает элемент, который не очень хорошая идея.
Ответы
Ответ 1
Я бы порекомендовал простой, вложенный цикл с 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;
Ответ 2
Используя LINQ, я думаю, что ваш код хорош с небольшими изменениями, которые не нуждаются в .ToList()
, просто вызовите расширение Count()
следующим образом:
int result = listOfLists.SelectMany(list => list).Distinct().Count();
Ответ 3
Чтобы подсчитать все элементы во всех списках в списке, вы можете использовать агрегирующие операторы:
int count = listOfLists.Sum(l => l.Distinct().Count());
Ответ 4
Я хотел бы получить шанс ответить на этот вопрос, просто чтобы выделить, когда мы должны использовать linq и когда классический для.
К сожалению, сегодня люди не очень заботятся о производительности, так как мы получили возможность работать на очень мощном компьютере. В любом случае попробуйте код ниже, и вы обнаружите, что Linq более чем в 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
}
отдельная версия, использующая для (например,).
В этом случае я создаю очень "узкое место", которое делает отчетливое, и оно еще быстрее.
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;
}
}
}
}