2010-10-19 8 views
25

Attualmente ho un programma di tipo di foglio di calcolo che conserva i suoi dati in un ArrayList di HashMaps. Senza dubbio sarai scioccato quando ti dico che questo non si è dimostrato ideale. L'overhead sembra utilizzare 5 volte più memoria dei dati stessi.Alternative di HashMap per la memorizzazione dei dati a efficienza di memoria

This question chiede informazioni sulle raccolte di raccolte efficienti e la risposta era utilizzare Google Collections. Il mio follow-up è "quale parte?". Ho letto la documentazione, ma non mi sembra che dia un buon senso di quali classi sono adatte a questo. (Sono aperto anche ad altre librerie o suggerimenti).

Quindi sto cercando qualcosa che mi consenta di archiviare dati di tipo spreadsheet densi con un sovraccarico di memoria minimo.

  • mie colonne sono attualmente riferimento da oggetti Field, righe dai loro indici e valori sono oggetti, quasi sempre stringhe
  • Alcune colonne avranno un sacco di valori ripetuti
  • operazioni principali sono per aggiornare o rimuovere record basati su valori di determinati campi e aggiunta/rimozione/combinazione di colonne

Sono a conoscenza di opzioni come H2 e Derby ma in questo caso non sto cercando di utilizzare un database incorporato.

EDIT: Se stai suggerendo le librerie, apprezzerei anche se potresti indicarmi una particolare classe o due in esse che si applicherebbero qui. Considerando che la documentazione di Sun di solito include informazioni su quali operazioni sono O (1), che sono O (N), ecc., Non sto vedendo molto di ciò nelle librerie di terze parti, e in realtà nessuna descrizione di quali classi sono più adatte a cosa .

+3

Ecco uno strumento che consente di confrontare l'impronta di memoria di qualsiasi struttura scelta: http://code.google.com/p/memory-measurer/ e vedere alcuni dati di esempio che ho ricavato da questo: http://code.google.com/p/memory-measurer/wiki/ElementCostInDataStructures –

+0

Sopra i collegamenti ha ottenuto il –

risposta

3

Quindi presumo che tu abbia una mappa di Map<ColumnName,Column>, in cui la colonna è in realtà qualcosa come ArrayList<Object>.

alcune possibilità -

  • Sei completamente sicuri che la memoria è un problema? Se sei solo generalmente preoccupato per le dimensioni, vale la pena di confermare che questo sarà davvero un problema in un programma in esecuzione. Ci vuole un sacco di file e mappe per riempire una JVM.

  • È possibile testare il set di dati con diversi tipi di mappe nelle raccolte. A seconda dei dati, è anche possibile inizializzare le mappe con combinazioni di dimensioni/fattori di carico preimpostate che potrebbero essere utili. In passato mi sono scontrato con questo, potresti ottenere una riduzione del 30% della memoria se sei fortunato.

  • Che dire di memorizzare i dati in una struttura dati a matrice singola (un'implementazione di libreria esistente o qualcosa come un wrapper attorno a un elenco di elenchi), con una singola mappa che associa le chiavi di colonna alle colonne di matrice?

+0

In realtà ogni record è una Mappa che Oggetto è il valore di ogni campo. Tutti i record sono contenuti in un ArrayList. La memoria è sicuramente un problema. Il caricamento di un file di 50 MB può talvolta superare 1 GB di memoria, il che mi porta a credere che la mia attuale implementazione sia orribilmente ingenua. –

+0

Farò dei test con diverse opzioni; quello che sto cercando di fare qui è restringere il campo a poche classi specifiche all'interno di librerie diverse che posso confrontare. –

+0

@bemace: stai riutilizzando gli stessi oggetti campo per ogni istanza di mappa dei record? –

11

Alcune colonne avranno un sacco di valori ripetuti

suggerisce immediatamente a me il possibile utilizzo del FlyWeight pattern, a prescindere dalla soluzione scelta per le vostre collezioni.

+1

mentre non ha affrontato il problema principale, questo mi ha spinto a capire finalmente come volare le stringhe correttamente in java. Grazie. http://stackoverflow.com/questions/3972841/when-is-it-beneficial-to-flyweight-strings-in-java –

4

collezioni Trove dovrebbero avere una particolare attenzione per lo spazio occupato (penso che anche hanno adattato strutture di dati se vi limitate a tipi primitivi) .. dare un'occhiata here.

Altrimenti puoi provare con Apache collections .. basta fare i benchmarks!

In anycase, se hai molti riferimenti intorno al stessi elementi tentare di disegnare qualche modello adatto (come flyweight)

+0

Trove non funzionerà per me perché non sto usando i primitivi. Vedo che HashedMap nelle raccolte di Apache è una "alternativa generica", ma non fornisce alcuna spiegazione di ciò che è diverso dalla normale HashMap.Hai qualche intuizione lì? –

+0

In realtà, vedo che menziona l'aggiunta della funzionalità di iterazione. Tuttavia, il mio problema è con le prestazioni non mancano funzionalità. –

1

mantiene i suoi dati in un ArrayList di HashMaps
Bene, questa parte sembra terribilmente inefficiente per me. Empty HashMap assegnerà già 16 * size of a pointer byte (16 sta per capacità iniziale predefinita), più alcune variabili per l'oggetto hash (14 + psize). Se hai un sacco di file scarsamente riempite, questo potrebbe essere un grosso problema.

Un'opzione consisterebbe nell'utilizzare un singolo hash grande con chiave composita (combinazione di riga e colonna). Sebbene ciò non renda molto efficaci le operazioni su intere file.

Inoltre, poiché non si menziona l'operazione di aggiunta di celle, è possibile creare hash con solo memoria interna necessaria (parametro initialCapacity).

Non so molto sulle raccolte di Google, quindi non posso aiutarvi. Inoltre, se trovi qualche ottimizzazione utile, ti preghiamo di postare qui! Sarebbe interessante sapere.

+0

Ti assicuro che * è * terribilmente inefficiente, ecco perché sono qui :) Nel mio caso le righe sparse non sono un grosso problema. –

0

Dalla tua descrizione, sembra che invece di un ArrayList di HashMaps voi piuttosto desidera un (Collegato) HashMap di ArrayList(ogni ArrayList sarebbe una colonna).

Aggiungerei una doppia mappa dal nome del campo al numero di colonna e alcuni getter/setter intelligenti che non generano mai IndexOutOfBoundsException.

È inoltre possibile utilizzare uno ArrayList<ArrayList<Object>> (fondamentalmente una matrice frastagliata dinamicamente crescente) e mantenere la mappatura al campo (colonna) nomi esterni.

Alcune colonne si hanno un sacco di valori ripetuti

Dubito che questo le cose, specialmente se sono stringhe, (sono interiorizzati) e la vostra collezione sarebbe memorizzare i riferimenti a loro.

2

Guava include un'interfaccia Table e un'implementazione basata su hash. Sembra un adattamento naturale al tuo problema. Si noti che questo è ancora contrassegnato come beta.

+4

Le implementazioni di Guava Table sono implementate come una mappa con valori Map. Di conseguenza, non ridurranno l'utilizzo della memoria. –

+0

@Jared Direi che dipenderà dall'implementazione di Map utilizzata? –

+0

@ Jared, hai ragione. – whiskeysierra

3

Supponendo che tutte le tue righe abbiano la maggior parte delle stesse colonne, puoi semplicemente utilizzare una matrice per ogni riga e una Mappa < ColumnKey, Integer> per cercare quali colonne si riferiscono a quale cella. In questo modo hai solo 4-8 byte di overhead per cella.

Se le stringhe vengono ripetute spesso, è possibile utilizzare un pool di stringhe per ridurre la duplicazione delle stringhe. I pool di oggetti per altri tipi immutabili possono essere utili per ridurre la memoria consumata.

MODIFICA: È possibile strutturare i dati come basati su righe o su colonne.Se le sue righe basate (una serie di celle per riga) aggiungendo/rimuovendo la riga è solo una questione di rimuovere questa riga. Se le sue colonne sono basate, puoi avere una matrice per colonna. Ciò può rendere molto più efficace la gestione dei tipi primitivi. vale a dire che puoi avere una colonna che è int [] e un'altra che è double [], è molto più comune per un'intera colonna avere lo stesso tipo di dati, piuttosto che avere lo stesso tipo di dati per un'intera riga.

Tuttavia, in entrambi i casi si sposteranno i dati che verranno ottimizzati per la modifica di righe o colonne e l'esecuzione di un'aggiunta/rimozione dell'altro tipo comporterà una ricostruzione dell'intero set di dati.

(Qualcosa che faccio è avere dati basati su righe e aggiungere colonne alla fine, assumendo che una riga non sia abbastanza lunga, la colonna ha un valore predefinito, questo evita una ricostruzione quando si aggiunge una colonna. colonna, ho un modo per ignorarlo)

+2

Se i valori del poster originale sono davvero densi, questo funzionerà alla grande. Oggetto [] [] o Elenco . Non scontate i vecchi standbys! Aggiungi Field # getNumber() e sei d'oro. Per quanto riguarda la duplicazione dei valori, l'interfaccia 'interner' delle librerie guava sembrerebbe adattarsi al progetto. –

+0

Sì, questo è quello che avevo in mente. –

+0

Non è una cattiva idea, ma come gestisci l'aggiunta e la rimozione di righe/colonne con quel tipo di struttura? –

1

Ho provato a utilizzare il SparseObjectMatrix2D dal progetto Colt. I miei dati sono piuttosto densi ma le loro classi Matrix non offrono alcun modo per ingrandirli, quindi sono andato con una matrice sparsa impostata alla dimensione massima.

Sembra utilizzare circa il 10% di memoria in meno e carica circa il 15% più veloce per gli stessi dati, oltre a offrire alcuni metodi di manipolazione intelligenti. Comunque sono interessato ad altre opzioni.

0

Perché non provare a utilizzare l'implementazione della cache come EHCache. Questo è risultato molto efficace per me, quando ho colpito la stessa situazione.
È possibile archiviare semplicemente la raccolta nell'implementazione di EHcache. Ci sono configurazioni come:

Maximum bytes to be used from Local heap. 

Una volta che i byte utilizzati dai vostri overflow dell'applicazione che configurate nella cache, quindi implementazione della cache si occupa di scrivere i dati sul disco. Inoltre è possibile configurare la quantità di tempo dopo la quale gli oggetti vengono scritti su disco utilizzando l'algoritmo Least Recent Used. Si può essere certi di evitare qualsiasi errore di memoria esaurita, utilizzando questo tipo di implementazioni della cache. Aumenta solo di poco le operazioni di I/O dell'applicazione.
Questa è solo una vista a volo d'uccello della configurazione. Ci sono molte configurazioni per ottimizzare i tuoi requisiti.

1

Chronicle Map potrebbe avere un sovraccarico inferiore a 20 byte per voce (vedere a test dimostrando ciò). Per confronto, l'overhead di java.util.HashMap varia da 37-42 byte con -XX:+UseCompressedOops a 58-69 byte senza oops compressi (reference).

Inoltre, Chronicle Map memorizza chiavi e valori off-heap, quindi non memorizza intestazioni oggetto, che non sono considerate come overhead di HashMap. Chronicle Map integrates con Chronicle-Values, una libreria per la generazione di implementazioni flyweight di interfacce, il modello suggested by Brian Agnew in un'altra risposta.

Problemi correlati