2016-06-21 24 views
5

Ho bisogno di usare un file grande che contiene stringhe String, String e perché voglio spedirlo con un JAR, ho optato per includere una versione serializzata e gzip nella cartella delle risorse del applicazione. Ecco come ho creato la serializzazione:Java: memorizzazione di una grande mappa in risorse

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

ho scelto di usare un HashMap<String,String>, il file risultante è 60MB e la mappa contiene circa 4 milioni di voci.

Ora, quando ho bisogno della mappa e ho deserializzare utilizzando:

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

Questa operazione richiede circa 10 ~ 15 secondi. C'è un modo migliore per archiviare una mappa così grande in un JAR? Lo chiedo perché uso anche la libreria Stanford CoreNLP che utilizza i file di modelli di grandi dimensioni ma sembra funzionare meglio a tale riguardo. Ho provato a individuare il codice in cui vengono letti i file del modello, ma ho rinunciato.

+0

Cosa richiede 10 ~ 15 secondi? Scrivere o leggere la mappa? Cosa vuoi migliorare? –

+0

Il suo secondo codice dice esplicitamente che richiede 10-15 sec per leggere il file – VLef

+0

verificarlo per aumentare le prestazioni della serializzazione e vedere il metodo flush. http://www.drdobbs.com/jvm/increase-java-serialization-performance/240159166 –

risposta

0

Che cosa si può fare è di applicare una tecnica proveniente dalla performance Java libro : La guida definitiva da Scott Oaks che memorizza in realtà il contenuto zip dell'oggetto in un array di byte quindi per questo abbiamo bisogno di un involucro classe che io chiamo qui MapHolder:

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

il codice sarà quindi essere semplicemente:

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

Come avrete notato, non è necessario zip più il contenuto è zippato internamente durante la serializzazione dell'istanza MapHolder.

+0

'FileUtils.getResource (" map.ser.gz ")' restituisce un InputStream del file contenuto nella cartella delle risorse all'interno del JAR. Ho usato la tua soluzione e vedo un aumento minimo di –

1

Il tuo problema è che i dati siano stati zippati. Memorizzalo come testo normale.

Il risultato della prestazione è molto probabilmente nel decomprimere il flusso. I vasi sono già zippati, quindi non c'è spazio per salvare il file zippato.

In sostanza:

  • Conservare il file in formato testo
  • Usa Files.lines(Paths.get("myfilenane.txt")) per lo streaming le linee
  • consumare ogni riga con codice minimo

Qualcosa di simile, assumendo dati sono in modulo key=value (come un file di proprietà):

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

Diniego: Codice non può compilare o il lavoro come è stato sfogliato in sul mio cellulare (ma c'è una ragionevole possibilità che funzionerà)

+0

Problemi multipli con questo. Non è un file sul file system, ma una risorsa all'interno del mio JAR, ma la lettura della linea non è un problema. L'utilizzo di stream e la suddivisione di ogni linea separatamente rendono la scansione più lenta della deserializzazione. –

+0

@eike So che è nel barattolo. Questo è il punto - è * già * zippato quando viene aggiunto al barattolo. OK, ho letto male la tua domanda (il file è un oggetto serializzato, non un file di testo), ma le basi della mia risposta si applicano ancora: non comprimere il file: mettilo nel barattolo così com'è. – Bohemian

+0

Sì, non lo zippare lo rende più veloce –

Problemi correlati