如何使用linq获得字典中项值内变量的不同计数
本文关键字:变量 linq 何使用 字典 | 更新日期: 2023-09-27 18:13:02
我有一个大约有4000万个项目的字典,我试图根据字典中每个keyvaluepair的值定义的一个长值来获得一个不同的计数。
我现在的做法:
int Total = (from c in Items select c.Value.Requester).Distinct().Count();
唯一的问题是,我的应用程序使用了大约3.9GB的ram,这种方法似乎是在复制它找到的那些项目(恰好是字典中95%的项目),所以在GC处理它之前,ram的使用会增加几个gb。
有没有一种方法可以在不复制的情况下获得不同的计数?
不,你不能那样做。它需要复制这些值,因为它需要记住它以前见过哪些值。
如果您有一个列表,其中的项按值排序。请求程序,然后您可以计数不同的值与一个单一的线性扫描而不复制。但是你没有。
如果你知道你的值在一个特定的范围内(例如1到100,000,000),你可以使用位数组编写一个更有效的内存算法。你可以创建一个100,000,000位的数组(一个320万int的数组),它只会消耗大约12.5兆字节,并使用它来存储你看到的值。
下面是一些你可以使用的代码:
// Warning: this scans the input multiple times!
// Rewriting the code to only use a single scan is left as an exercise
// for the reader.
public static int DistinctCount(this IEnumerable<int> values)
{
int min = values.Min();
int max = values.Max();
uint[] bitarray = new uint[(max - min + 31) / 32];
foreach (int value in values)
{
int i = (value - min) / 32;
int j = (value - min) % 32;
bitarray[i] |= (uint)(1 << j);
}
uint count = 0;
for (int i = 0; i < bitarray.Length; ++i)
{
uint bits = bitarray[i];
while (bits != 0)
{
count += bits & 1;
bits >>= 1;
}
}
return (int)count;
}
像这样使用:
int Total = (from c in Items select c.Value.Requester).DistinctCount();
您可能需要重新考虑如何创建您的字典。如果您从文件构建它,您可能希望每次读取较小的块。为了获得不同的项,可以从字典文件的每个块开始向HashSet<>
添加项。HashSet<>
的最终大小将是不同项目的数量。这种方法可能仍然很慢,因为每次向集合添加一个值时,集合都需要做一些工作来确保一个值不已经存在。
我会从马克的回答中得到一些提示:确保你的输入在你读到你的应用程序之前是排序的:如果你的数据是排序的,你可以在一次传递中计算不同的项目(你基本上计算n
的值与n + 1
的值不同的次数。
虽然它在大多数情况下实际上是无用的,但在技术上可以使用简单的O(n^2)算法(这将花费一些时间来执行40 000 000项)
public static int DistinctCount(this IEnumerable<int> values)
{
int max = values.Max();
int last = int.MinValue;
int result = 0;
do
{
int current = int.MaxValue;
foreach (int value in values)
{
if (value < current && value > last)
{
current = value;
}
}
result++;
last = current;
} while (last != max);
return result;
}
正如其他人已经指出的那样,你使用的结构不能在不复制的情况下实现你想要的…
如果你真的需要在你目前的结构中这样做,我认为你将不得不引入一些冗余……也就是说,当你从这个"大字典"中插入/删除条目时,维护第二个相当小的字典,它只是用计数保存不同的值(注意多线程问题)…
作为替代:
Use a Database…如果需要的话,还有内存db…但我很确定基于磁盘的数据库可以胜任这项任务(每小时4000万,每秒不到20K)…我更喜欢甲骨文…但SQLite, Postgres等也绝对没问题…你可以使用SQLite作为一个纯粹的"内存DB",如果你想和/或者你可以创建一个RAM磁盘,并把DB文件。