2016-06-21 10 views
5

Ich muss eine große Datei verwenden, die String, String - Paare enthält, und weil ich es mit einem JAR versenden möchte, entschied ich mich, eine serialisierte und gziped Version in den Ressourcenordner der Anwendung. Dies ist, wie ich die Serialisierung erstellt:Java: Speichern einer großen Karte in Ressourcen

ObjectOutputStream out = new ObjectOutputStream(
      new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(OUT_FILE_PATH, false)))); 
out.writeObject(map); 
out.close(); 

ich HashMap<String,String> verwenden gewählt haben, ist die resultierende Datei 60MB und die Karte enthält etwa 4 Millionen Einträge.

Nun, wenn ich brauche die Karte, und ich deserialisieren es mit:

final InputStream in = FileUtils.getResource("map.ser.gz"); 
final ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new GZIPInputStream(in))); 
map = (Map<String, String>) ois.readObject(); 
ois.close(); 

dies dauert etwa 10 bis 15 Sekunden. Gibt es eine bessere Möglichkeit, eine so große Karte in einem JAR zu speichern? Ich frage, weil ich auch die Stanford CoreNLP-Bibliothek verwende, die selbst große Modelldateien verwendet, aber in dieser Hinsicht besser zu funktionieren scheint. Ich habe versucht, den Code zu finden, wo die Modelldateien gelesen werden, gab aber auf.

+0

Was dauert 10 ~ 15 Sekunden? Schreiben oder Lesen der Karte? Was möchtest du verbessern? –

+0

Sein zweiter Code sagt ausdrücklich, dass es 10-15sec dauert, um die Datei – VLef

+0

zu lesen, um die Serialisierungsleistung zu erhöhen und die Flush-Methode zu sehen. http://www.drdobbs.com/jvm/increase-java-serialization-performance/240159166 –

Antwort

0

Was Sie tun können, ist eine Technik zur Anwendung eines von dem Java Performance-Buch kommen: The Definitive Guide von Scott Oaks, die tatsächlich den komprimierten Inhalt des Objekts in eine Byte-Array speichert so dafür haben wir einen Wrapper müssen Klasse, die ich hier MapHolder nennen:

public class MapHolder implements Serializable { 
    // This will contain the zipped content of my map 
    private byte[] content; 
    // My actual map defined as transient as I don't want to serialize its 
    // content but its zipped content 
    private transient Map<String, String> map; 

    public MapHolder(Map<String, String> map) { 
     this.map = map; 
    } 

    private void writeObject(ObjectOutputStream out) throws IOException { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     try (GZIPOutputStream zip = new GZIPOutputStream(baos); 
      ObjectOutputStream oos = new ObjectOutputStream(
       new BufferedOutputStream(zip))) { 
      oos.writeObject(map); 
     } 
     this.content = baos.toByteArray(); 
     out.defaultWriteObject(); 
     // Clear the temporary field content 
     this.content = null; 
    } 

    private void readObject(ObjectInputStream in) throws IOException, 
     ClassNotFoundException { 
     in.defaultReadObject(); 
     try (ByteArrayInputStream bais = new ByteArrayInputStream(content); 
      GZIPInputStream zip = new GZIPInputStream(bais); 
      ObjectInputStream ois = new ObjectInputStream(
       new BufferedInputStream(zip))) { 
      this.map = (Map<String, String>) ois.readObject(); 
      // Clean the temporary field content 
      this.content = null; 
     } 
    } 

    public Map<String, String> getMap() { 
     return this.map; 
    } 
} 

Der Code wird dann einfach:

final ByteArrayInputStream in = new ByteArrayInputStream(
    Files.readAllBytes(Paths.get("/tmp/map.ser")) 
); 
final ObjectInputStream ois = new ObjectInputStream(in); 
MapHolder holder = (MapHolder) ois.readObject(); 
map = holder.getMap(); 
ois.close(); 

Wie Sie vielleicht bemerkt haben, die Sie nicht Zip mehr den Inhalt es ist intern gezippt während der Serialisierung der MapHolder Instanz.

+0

'FileUtils.getResource (" map.ser.gz ")' gibt einen InputStream der Datei zurück, die sich im Ressourcenordner innerhalb der JAR befindet. Ich habe Ihre Lösung verwendet und sehe eine minimale Beschleunigung –

1

Ihr Problem ist, Sie die Daten gezippt. Speichere es als Klartext.

Der Performance-Hit ist höchstwahrscheinlich das Entpacken des Streams. Gläser sind bereits gezippt, so gibt es keinen Platz sparenden Speichern der Datei gezippt.

Grundsätzlich:

  • Speichern Sie die Datei im Klartext
  • Verwenden Files.lines(Paths.get("myfilenane.txt")) die Linien
  • Verbrauchen jede Zeile mit minimalem Code

etwas zu streamen, wobei angenommen Daten in Formular key=value (wie eine Eigenschaftendatei):

Map<String, String> map = new HashMap<>(); 
Files.lines(Paths.get("myfilenane.txt")) 
    .map(s -> s.split("=")) 
    .forEach(a -> map.put(a[0], a[1])); 

Haftungsausschluss: Code kann nicht kompiliert werden oder arbeiten, wie es war auf meinem Handy blättert in (aber es gibt eine gute Chance, es wird funktionieren)

+0

Mehrere Probleme mit diesem. Es ist keine Datei im Dateisystem, sondern eine Ressource in meinem JAR, aber Zeilenlesen ist kein Problem. Das Verwenden von Streams und das separate Teilen jeder Zeile macht dies tatsächlich langsamer als die Deserialisierung. –

+0

@eike Ich weiß, dass es im Glas ist. Das ist der Punkt - es ist * bereits * gezippt, wenn es zum Glas hinzugefügt wird. OK, ich habe Ihre Frage falsch gelesen (die Datei ist ein serialisiertes Objekt, keine Textdatei), aber die Grundlagen meiner Antwort gelten immer noch: Zippen Sie die Datei nicht - legen Sie sie in das Glas wie sie ist. – Bohemian

+0

Ja, nicht zippen macht es schneller –

Verwandte Themen