如何使用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。

有没有一种方法可以在不复制的情况下获得不同的计数?

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

不,你不能那样做。它需要复制这些值,因为它需要记住它以前见过哪些值。

如果您有一个列表,其中的项按值排序。请求程序,然后您可以计数不同的值与一个单一的线性扫描而不复制。但是你没有。

如果你知道你的值在一个特定的范围内(例如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文件。