Это можно решить элегантно, используя Linq:
public static void Main(string[] args)
{
List<int> list = new List<int> { 2, 3, 4, 5, 2, 4, 6, 2, 4, 7, 3, 8, 2 };
var grouping = list
.GroupBy(x => x)
.Select(x => new { Item = x.Key, Count = x.Count()});
foreach (var item in grouping)
Console.WriteLine("Item {0} has count {1}", item.Item, item.Count);
}
Внутренне он, вероятно, использует хеширование для разбиения списка, но код скрывает внутренние детали - здесь мы только сообщаем ему , что рассчитать. Компилятор / среда выполнения могут свободно выбирать как для его вычисления и оптимизировать по своему усмотрению. Благодаря Linq этот же код будет эффективно выполняться независимо от того, будет ли список запущен в памяти или если список находится в базе данных. В реальном коде вы должны использовать это, но я думаю, вы хотите знать, как это работает внутри.
Более императивный подход, который демонстрирует фактический алгоритм, следующий:
List<int> list = new List<int> { 2, 3, 4, 5, 2, 4, 6, 2, 4, 7, 3, 8, 2 };
Dictionary<int, int> counts = new Dictionary<int, int>();
foreach (int item in list)
{
if (!counts.ContainsKey(item))
{
counts[item] = 1;
}
else
{
counts[item]++;
}
}
foreach (KeyValuePair<int, int> item in counts)
Console.WriteLine("Item {0} has count {1}", item.Key, item.Value);
Здесь вы можете видеть, что мы перебираем список только один раз, сохраняя счет для каждого элемента, который мы видим в пути. Это было бы плохой идеей, если бы элементы были в базе данных, поэтому для реального кода предпочтительнее использовать метод Linq.