2013-10-16 5 views
5

Ich habe eine dictionary<string, int[]>, die ich so effizient wie möglich von der Festplatte speichern und abrufen muss.Der effizienteste Weg, um ein Wörterbuch in C# zu speichern/abzurufen?

Die Schlüssellänge (Zeichenfolge) wird normalerweise von 1 bis 60 Zeichen (Unicode) variieren, könnte aber diese Länge überschreiten (dies ist jedoch geringfügig und diese Werte könnten verworfen werden). Ganzzahlen im Array liegen im Bereich von 1 bis 100 Millionen. (In der Regel 1 bis 5 M)

Meine erste Idee, ein Format mit Trennzeichen zu verwenden, war:

key [tab] int,int,int,int,... 
key2 [tab] int,int,int,int,... 
... 

und das Wörterbuch zu laden, wie folgt:

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

Es funktioniert, aber gehen über die Mögliche Größenanforderungen, es ist offensichtlich, dass diese Methode nie gut genug skalieren wird.

Gibt es eine Standardlösung für dieses Problem oder muss ich den Algorithmus vollständig überarbeiten?


Edit: Ich bin ein wenig embarassed es zugeben, aber ich wusste nicht, Wörterbücher nur auf binäre serialisiert werden. Ich habe es getestet und es ist genau das, was ich gebraucht habe.

Hier ist der Code (Vorschläge willkommen)

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

Mit einem Wörterbuch von 100k Einträgen mit einer Reihe von 4k ganzen Zahlen, nimmt Serialisierung 14 Sekunden und Deserialisierung 10 Sekunden und die resultierende Datei ist 1,6 GB.

@Patryk: Bitte konvertieren Sie Ihren Kommentar zu einer Antwort, damit ich es als genehmigt markieren kann.

+1

Mit "effizient" meinst du "size-effizient"? – Stefan

+0

@Stefan - Größe/Geschwindigkeit scheint kein Problem zu sein, da OP es in Textdatei speichert ... Aber in der Tat ist es notwendig zu wissen, welche Art von "gut genug skalieren" benötigt wird, bevor es beantwortet werden kann. –

+1

Wenige Seitennotizen; Anstatt Ihre Liste außerhalb der Schleife zu halten und sie ständig zu löschen, definieren Sie einfach die Liste innerhalb der Schleife. Wenn Sie eine Zeichenfolge ohne Trennzeichen aufteilen, wird nur ein Array der Größe 1 mit diesem Wert zurückgegeben. Sie müssen also nicht überprüfen, ob die Zeichenfolge "," enthält. Teilen Sie sie jedes Mal auf und fügen Sie alle Werte zur Liste hinzu "Alles" ist nur eins. Müssen Sie das Array sortieren? Wenn Sie eine vorhandene Struktur neu erstellen, warum sind sie nicht bereits sortiert? – Servy

Antwort

0

Die Dictionary<TKey, TValue> ist als [Serializable] markiert (und implementiert ISerializable) die .

Das bedeutet, dass Sie z.B. BinaryFormatter, um binäre Serialisierung und Deserialisierung zu und von einem Stream auszuführen. Sprich, FileStream. :)

1

Ich vermute, Sie möchten den Speicherbedarf während der Belastung reduzieren. Im Moment laden Sie alles in den Speicher eines Arrays und kopieren alles in ein Wörterbuch. Bis das ursprüngliche Array den Bereich verlässt und Müll gesammelt wird, wird es einen Zeitraum geben, der ungefähr die doppelte Speicherbelegung benötigt. Wenn es eine sehr große Datei ist, dann könnte das eine Menge sein ... wenn es nur ein paar Megabyte ist, ist das keine große Sache.

Wenn Sie dies tun möchten, effizient können Sie die Daten aus einem Stream wie so lesen:

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

Oder können Sie die Verknüpfung verwenden Jim vorgeschlagen:

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

Dies macht eine strenge Annahmen über das Dateiformat. Beachten Sie, dass jede Zeile das Format key,int1,int2,int3,int4,... hat und der Schlüssel kein Komma enthält. Jede Zeile muss auch in einem Environment.NewLine Zeichen enden.

Obwohl es erwähnenswert ist, dass Sie die Tatsache berücksichtigen sollten, dass Ihr aktueller Code zwar nicht besonders effizient ist, aber nicht Ihr größter Flaschenhals. Die Datei Lesegeschwindigkeit ist normalerweise der größte Flaschenhals. Wenn Sie tatsächlich Leistungsprobleme mit Ihrem Code haben, hat es wahrscheinlich einfach damit zu tun, dass Sie synchron aus der Datei lesen. Jede Datei-E/A sollte asynchron in einer Anwendung mit einer Benutzerschnittstelle durchgeführt werden.

+1

+1. Beachten Sie, dass Sie eine Menge dieses Codes durch 'foreach (Zeichenfolgelinie in File.ReadLines (fileName))' ersetzen können –

Verwandte Themen