2013-10-16 52 views
5

我有一個dictionary<string, int[]>,我需要儘可能有效地從磁盤存儲和檢索。在C#中存儲/檢索字典的最有效方法?

密鑰長度(字符串)通常會在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:請將您的評論轉換爲答案,以便我可以將其標記爲已批准。

+1

「有效地」你的意思是「尺寸效率」? – Stefan

+0

@Stefan - 大小/速度似乎不是一個問題,因爲OP將它存儲在文本文件中......但的確需要知道什麼類型的「規模足夠好」才需要回答。 –

+1

很少有旁註;而不是讓你的列表在循環之外並不斷清理它,只需在循環內定義列表。分割一個沒有分隔符的字符串只會返回一個大小爲1的數組,因此您不需要檢查字符串是否包含',',只是每次都分割並將所有值添加到列表中,即使「全部」只是一個。你需要對數組進行排序嗎?如果您正在重新創建現有結構,爲什麼他們還沒有排序? – Servy

回答

0

Dictionary<TKey, TValue>標記爲[Serializable](和實現ISerializable)其中can be seen here

這意味着您可以使用例如BinaryFormatter執行二進制序列化和反序列化到和來自一個流。說,FileStream。 :)

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()); 
    }  
} 

或者你也可以使用快捷鍵吉姆建議:

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。請注意,您可以用'foreach(File.ReadLines(fileName)中的字符串行)替換大量代碼' –

相關問題