如何使用linq在字典中的项的值内获得不同的变量计数

时间:2011-11-01 05:43:51

标签: c# linq

我有一个大约有4千万个项目的字典,我试图根据字典中每个keyvaluepair值中定义的ulong来获得一个独特的计数。

我目前正在这样做:

int Total = (from c in Items select c.Value.Requester).Distinct().Count();

唯一的问题是我的应用程序使用了大约3.9GB的ram,而且这个方法似乎正在复制它找到的那些项目(这恰好是字典中约95%的项目)所以在GC开始处理所有内容之前,ram的使用量增加了几千兆字节。

有没有办法在不制作副本的情况下获得明确的计数?

4 个答案:

答案 0 :(得分:2)

不,你不能这样做。它需要复制值,因为它需要记住它之前看到的值。

如果您有一个列表,其中的项目按Value.Requester排序,那么您可以使用单个线性扫描计算不同的值而无需复制。但你没有那个。

如果您知道您的值位于特定范围内(例如1到100,000,000),您可以使用位数组编写更高效的内存算法。你可以创建一个100,000,000比特的数组(一个320万字节的数组),它只消耗大约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();

答案 1 :(得分:0)

您可能需要重新考虑如何创建字典。如果要从文件构建它,您可能希望一次读取较小的块。要获取不同的项目,您可以从字典文件的每个块开始向HashSet<>添加项目。 HashSet<>的最终大小将是不同项目的数量。这种方法可能仍然很慢,因为集合需要做的工作是确保每次向集合添加值时都不存在值。

我会从Mark的答案中得到一些提示:确保在将输入读入应用程序之前对输入进行排序:如果数据已排序,您可以在一次通过中计算不同的项目(基本上计算值的次数) n的值与n + 1的值不同。

答案 2 :(得分:0)

正如其他人已经指出的那样,你使用的结构无法复制而无法复制......

如果您确实需要使用当前结构执行此操作,我认为您将不得不引入一些冗余...即,当您从这个“大词典”插入/删除项目时,保留第二个相当小的一个只保持不同的带有计数的值(请注意多线程问题)......

至于替代方案:

使用数据库...如果需要有内存数据库......但我非常确定基于磁盘的数据库不仅仅是完成任务(每小时4000万小时)每秒20K)...我更像是一个甲骨文家伙......但SQLite,Postgres等对此也是绝对正确的......如果你想要的话,可以使用SQLite作为纯粹的“内存数据库” /或者您可以创建一个RAM磁盘并将数据库文件放在那里。

答案 3 :(得分:0)

虽然在大多数情况下它实际上没用,但是在技术上可以使用简单的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;
    }