C# 用C语言存储/检索词典的最有效方法是什么?
我有一本字典,我需要尽可能高效地从磁盘上存储和检索它 键长度字符串通常在1到60个字符的unicode范围内变化,但可能会超过该长度,但这是一个边缘值,这些值可能会被丢弃。数组中的整数将在1到1亿之间。通常为1至5米 我的第一个想法是使用分隔格式:C# 用C语言存储/检索词典的最有效方法是什么?,c#,performance,file-access,C#,Performance,File Access,我有一本字典,我需要尽可能高效地从磁盘上存储和检索它 键长度字符串通常在1到60个字符的unicode范围内变化,但可能会超过该长度,但这是一个边缘值,这些值可能会被丢弃。数组中的整数将在1到1亿之间。通常为1至5米 我的第一个想法是使用分隔格式: key [tab] int,int,int,int,... key2 [tab] int,int,int,int,... ... 并按如下方式加载字典: string[] Lines = File.ReadAllLines(sIndexName).
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
@帕特里克:请将您的评论转换为答案,以便我将其标记为已批准。我猜您希望在加载期间减少内存占用。现在,您正在将所有内容加载到数组中的内存中,然后将所有内容复制到字典中。在原始数组超出作用域并被垃圾回收之前,将有一段时间需要大约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());
}
这对文件格式做出了一些严格的假设。值得注意的是,每一行都是格式键int1、int2、int3、int4,。。。而且密钥不包含逗号。每行还必须以Environment.NewLine字符结尾
虽然值得注意的是,你应该考虑这样一个事实,即当你的当前代码不是非常有效的时候,它不是你的主要瓶颈。文件读取速度通常是最大的瓶颈。如果您的代码确实遇到性能问题,那么很可能与您同步读取文件有关。任何文件I/O都应该在具有用户界面的应用程序中异步完成。字典被标记为[Serializable],并实现ISerializable
这意味着您可以使用,例如,执行与流之间的二进制序列化和反序列化。比如说,文件流: “高效”是指大小高效?@Stefan-大小/速度似乎不是一个问题,因为OP将其存储在文本文件中。。。但事实上,在回答这个问题之前,有必要充分了解需要什么样的量表;与其让列表在循环之外并不断清除它,不如在循环内部定义列表。拆分一个没有分隔符的字符串只会返回一个大小为1且具有该值的数组,因此您无需检查该字符串是否包含,只需每次拆分该字符串并将所有值添加到列表中,即使所有值仅为一。您需要对数组进行排序吗?如果你正在创建一个现有的结构,为什么它们还没有排序?@AlexeiLevenkov一个非常有效的问题是这里优化了什么,程序的速度,文件的大小,代码的可读性等等?注意:字典被标记为[可序列化],因此,您可以使用BinaryFormatter轻松地将其序列化/反序列化为二进制。我不知道它是否适合您的用例…+1。请注意,您可以用File.ReadLinesfileName中的foreach字符串行替换大量代码