我有一个dictionary<string, int[]>
,我需要从磁盘中尽可能有效地存储和检索。
密钥长度(字符串)通常在1到60个字符(unicode)之间变化,但可能超过该长度(但这是边缘的,可以丢弃这些值)。数组中的整数将在1到1亿之间。 (通常为1至5M)
我的第一个想法是使用分隔格式:
key [tab] int,int,int,int,...
key2 [tab] int,int,int,int,...
...
并按如下方式加载字典:
string[] Lines = File.ReadAllLines(sIndexName).ToArray();
string[] keyValues = new string[2];
List<string> lstInts = new List<string>();
// Skip the header line of the index file.
for (int i = 1; i < Lines.Length; i++)
{
lstInts.Clear();
keyValues = Lines[i].Split('\t');
if (keyValues[1].Contains(','))
{
lstInts.AddRange(keyValues[1].Split(','));
}
else
{
lstInts.Add(keyValues[1]);
}
int[] iInts = lstInts.Select(x => int.Parse(x)).ToArray();
Array.Sort(iInts);
dic.Add(keyValues[0], iInts);
}
虽然有效但是超出了潜在的尺寸要求,显然这种方法永远不会很好地扩展。
是否有解决此问题的现成解决方案,还是需要完全重写算法?
编辑:我有点尴尬承认它,但我不知道字典可以被序列化为二进制。我给了它一个测试运行,这几乎是我需要的。
以下是代码(建议欢迎)
public static void saveToFile(Dictionary<string, List<int>> dic)
{
using (FileStream fs = new FileStream(_PATH_TO_BIN, FileMode.OpenOrCreate))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, dic);
}
}
public static Dictionary<string, List<int>> loadBinFile()
{
FileStream fs = null;
try
{
fs = new FileStream(_PATH_TO_BIN, FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
return (Dictionary<string, List<int>>)bf.Deserialize(fs);
}
catch
{
return null;
}
}
使用100k条目的字典,每个条目具有4k个整数数组,序列化需要14秒,反序列化需要10秒,结果文件为1.6gb。
@Patryk:请将您的评论转换为答案,以便我将其标记为已批准。
答案 0 :(得分:1)
我猜你想在加载过程中减少内存占用。现在,您将所有内容加载到数组中的内存中,然后将所有内容复制到字典中。在原始阵列超出范围并进行垃圾收集之前,将会有一段时间,大约需要2倍的内存使用量。如果它是一个非常大的文件,那么这可能会很多......如果它只有几兆字节,那不是什么大不了的事。
如果您想更有效地执行此操作,您可以从流中读取数据,如下所示:
string fileName = @"C:\...";
var dict = new Dictionary<string, int[]>();
using (var fs = new FileStream(fileName, FileMode.Open))
using (var reader = new StreamReader(fs))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var values = line.Split(',');
dict.Add(values[0], values.Skip(1).Select(x => Convert.ToInt32(x)).ToArray());
}
}
或者您可以使用Jim建议的快捷方式:
string fileName = @"C:\...";
var dict = new Dictionary<string, int[]>();
foreach (string line in File.ReadLines(fileName))
{
var values = line.Split(',');
dict.Add(values[0], values.Skip(1).Select(x => Convert.ToInt32(x)).ToArray());
}
这对文件格式做了一些严格的推定。值得注意的是,每一行的格式为key,int1,int2,int3,int4,...
,并且该键不包含逗号。每一行也必须以Environment.NewLine
字符结尾。
虽然值得注意的是,您应该考虑这样一个事实:虽然您当前的代码不是非常有效,但它不是您的主要瓶颈。文件读取速度通常是最大的瓶颈。如果您实际遇到代码性能问题,则很可能只是与您同步读取文件有关。任何文件I / O都应该在具有用户界面的应用程序中异步完成。
答案 1 :(得分:1)
Dictionary<TKey, TValue>
被标记为[Serializable]
(并实施ISerializable
)can be seen here。
这意味着你可以使用例如BinaryFormatter
对流执行二进制序列化和反序列化。说,FileStream
。 :)