web-dev-qa-db-fra.com

Alternatives HashMap pour un stockage de données efficace en mémoire

J'ai actuellement un programme de type tableur qui conserve ses données dans un ArrayList de HashMaps. Vous serez sans doute choqué quand je vous dirai que cela n’est pas idéal. La surcharge semble utiliser 5 fois plus de mémoire que les données elles-mêmes.

Cette question concerne des bibliothèques de collections efficaces, et la réponse a été d'utiliser Google Collections. _ {Mon suivi est "quelle partie?"}. J'ai lu la documentation, mais je ne pense pas que cela donne une très bonne idée des classes qui conviennent le mieux. (Je suis également ouvert à d'autres bibliothèques ou suggestions).

Je cherche donc quelque chose qui me permette de stocker des données de type feuille de calcul denses avec une surcharge de mémoire.

  • Mes colonnes sont actuellement référencées par des objets Champ, les lignes par leurs index et les valeurs sont des objets, presque toujours des chaînes.
  • Certaines colonnes auront beaucoup de valeurs répétées
  • les opérations principales consistent à mettre à jour ou à supprimer des enregistrements en fonction des valeurs de certains champs, ainsi qu'à ajouter/supprimer/combiner des colonnes

Je connais des options comme H2 et Derby, mais dans ce cas, je ne cherche pas à utiliser une base de données intégrée.

EDIT: Si vous proposez des bibliothèques, je vous serais également reconnaissant de m'indiquer une classe ou deux en particulier qui s'appliqueraient ici. Alors que la documentation de Sun comprend généralement des informations sur les opérations O (1), O (N), etc., je n'en vois pas beaucoup dans les bibliothèques tierces, ni même une description des classes les mieux adaptées à quoi. .

26
Brad Mace

Donc, je suppose que vous avez une carte de Map<ColumnName,Column>, où la colonne est en réalité quelque chose comme ArrayList<Object>

Quelques possibilités - 

  • Êtes-vous complètement sûr que la mémoire est un problème? Si vous êtes simplement inquiet au sujet de la taille, il serait utile de confirmer que cela posera vraiment problème dans un programme en cours d'exécution. Il faut énormément de lignes et de cartes pour remplir une machine virtuelle Java. 

  • Vous pouvez tester votre ensemble de données avec différents types de cartes dans les collections. En fonction de vos données, vous pouvez également initialiser des cartes avec des combinaisons prédéfinies de taille/facteur de charge pouvant vous aider. J'ai déjà joué avec cela, vous pourriez obtenir une réduction de mémoire de 30% si vous avez de la chance.

  • Qu'en est-il de stocker vos données dans une structure de données semblable à une matrice (une implémentation de bibliothèque existante ou un encapsuleur autour d'une liste de listes), avec une seule carte qui mappe les clés de colonne aux colonnes de matrice? 

4
Steve B.

Certaines colonnes auront beaucoup de valeurs répétées

me suggère immédiatement l’utilisation possible du motif FlyWeight , quelle que soit la solution choisie pour vos collections.

11
Brian Agnew

Les collections Trove devraient porter une attention particulière à l'espace occupé (je pense qu'elles ont également des structures de données personnalisées si vous vous en tenez à des types primitifs) .. jetez un oeil ici .

Sinon, vous pouvez essayer avec collections Apache .. faites vos tests!

Dans tous les cas, si vous avez plusieurs références autour des mêmes éléments, essayez de concevoir un motif adapté (comme flyweight )

5
Jack

En supposant que toutes vos lignes contiennent presque les mêmes colonnes, vous pouvez simplement utiliser un tableau pour chaque ligne et un objet Map <ColumnKey, Integer> pour rechercher les colonnes qui font référence à quelle cellule. De cette façon, vous ne disposez que de 4 à 8 octets de temps système par cellule.

Si les chaînes sont souvent répétées, vous pouvez utiliser un pool de chaînes pour réduire la duplication des chaînes. Les pools d'objets pour d'autres types immuables peuvent être utiles pour réduire la mémoire utilisée.

EDIT: Vous pouvez structurer vos données en lignes ou en colonnes. Si ses lignes sont basées (un tableau de cellules par ligne), l'ajout/la suppression de la ligne consiste simplement à supprimer cette ligne. Si ses colonnes sont basées, vous pouvez avoir un tableau par colonne. Cela peut rendre la gestion des types primitifs beaucoup plus efficace. Par exemple, vous pouvez avoir une colonne qui est int [] et une autre qui est double [], il est beaucoup plus courant qu'une colonne entière ait le même type de données, plutôt que d'avoir le même type de données pour une ligne entière.

Cependant, quelle que soit la structure utilisée, les données seront optimisées pour une modification de ligne ou de colonne. Effectuer une opération d'ajout/suppression de l'autre type entraînera une reconstruction de l'ensemble du jeu de données.

(Quelque chose que j’ai, c’est d’avoir des données basées sur des lignes et d’ajouter des colonnes à la fin, en supposant que si une ligne est trop longue, la colonne a une valeur par défaut, cela évite une reconstruction lors de l’ajout d’une colonne. Plutôt que de supprimer une colonne, j’ai un moyen de l'ignorer)

3
Peter Lawrey

Guava inclut une interface Table et une implémentation basée sur le hachage. Semble un ajustement naturel à votre problème. Notez que cela est toujours marqué en tant que bêta.

2
whiskeysierra

Chronicle Map pourrait avoir des frais généraux de moins de 20 octets par entrée (voir un test prouvant ceci). A titre de comparaison, la surcharge de Java.util.HashMap varie de 37 à 42 octets avec -XX:+UseCompressedOops à 58 à 69 octets sans oops compressé ( reference ).

De plus, Chronicle Map stocke les clés et les valeurs en dehors du tas, de sorte qu'il ne stocke pas les en-têtes d'objet, qui ne sont pas comptabilisés dans la surcharge ci-dessus de HashMap. Chronicle Map intègre avec Chronicle-Values ​​ , une bibliothèque permettant de générer des implémentations d'interfaces flyweight, le motif suggéré par Brian Agnew dans une autre réponse.

1
leventov

J'ai essayé d'utiliser le SparseObjectMatrix2D du projet Colt . Mes données sont assez denses mais leurs classes Matrix n'offrent vraiment aucun moyen de les agrandir, alors je suis allé avec une matrice creuse réglée à la taille maximale.

Il semble utiliser environ 10% de mémoire en moins et se charge d'environ 15% plus rapidement pour les mêmes données, tout en offrant des méthodes de manipulation astucieuses. Toujours intéressé par d'autres options cependant.

1
Brad Mace

conserve ses données dans une ArrayList of HashMaps
Bien, cette partie me semble terriblement inefficace. Vide HashMap allouera déjà 16 * size of a pointer octets (16 correspond à la capacité initiale par défaut), plus quelques variables pour l'objet de hachage (14 + psize). Si vous avez beaucoup de rangées peu remplies, cela pourrait poser un gros problème.

Une option serait d'utiliser un seul grand hachage avec une clé composite (combinant ligne et colonne). Bien que cela ne rende pas les opérations sur des lignes entières très efficaces. 

De plus, puisque vous ne mentionnez pas l'opération d'ajout de cellule, vous pouvez créer des hachages avec uniquement le stockage interne nécessaire (paramètre initialCapacity).

Je ne connais pas grand chose aux collections Google, je ne peux donc pas vous aider. De plus, si vous trouvez une optimisation utile, merci de poster ici! Ce serait intéressant de savoir.

1
Nikita Rybak

D'après votre description, il semble qu'au lieu d'un tableau de tableaux de hachages, vous préférez un tableau de hachages (lié) d'arrayList (chaque tableau de tableaux serait une colonne).

J'ajouterais une double carte de nom de champ à numéro de colonne, et quelques getters/setters intelligents qui ne jettent jamais IndexOutOfBoundsException.

Vous pouvez également utiliser un ArrayList<ArrayList<Object>> (une matrice à croissance irrégulière en dents de scie) et conserver le mappage avec les noms de champs (colonnes) à l'extérieur.

Certaines colonnes auront beaucoup de valeurs répétées

Je doute que cela soit important, spécialement s’il s’agit de Strings (elles sont internalisées) et que votre collection stockera des références à celles-ci.

0
leonbloy

Pourquoi n'essayez-vous pas d'utiliser une implémentation de cache telle que EHCache . Cela s'est avéré très efficace pour moi, lorsque je me suis retrouvé dans la même situation.
Vous pouvez simplement stocker votre collection dans l’implémentation d’EHcache . Il existe des configurations telles que:

Maximum bytes to be used from Local heap.

Une fois que les octets utilisés par votre application dépassent ceux configurés dans le cache, l'implémentation du cache se charge de l'écriture des données sur le disque. Vous pouvez également configurer la durée après laquelle les objets sont écrits sur le disque à l'aide de l'algorithme Least Recent Used . Vous pouvez être sûr d'éviter toute erreur de mémoire insuffisante en utilisant ce type d'implémentations de cache . légèrement les opérations IO de votre application.
Ceci est juste une vue à vol d'oiseau de la configuration. Il existe de nombreuses configurations pour optimiser vos besoins.

0
NiranjanBhat