2010-10-19 13 views
25

Actualmente tengo un programa de tipo hoja de cálculo que conserva sus datos en una Lista de Arratos de HashMaps. Sin duda se sorprenderá cuando le digo que esto no ha resultado ideal. La sobrecarga parece usar 5 veces más memoria que los datos en sí.Alternativas HashMap para el almacenamiento de datos con memoria eficiente

This question pregunta acerca de las bibliotecas de colecciones eficientes, y la respuesta fue usar Google Collections. Mi seguimiento es "¿qué parte?". He estado leyendo la documentación, pero no creo que dé una buena idea de qué clases son adecuadas para esto. (También estoy abierto a otras bibliotecas o sugerencias).

Así que estoy buscando algo que me permita almacenar datos densos de tipo hoja de cálculo con una sobrecarga de memoria mínima.

  • Mis columnas están actualmente referenciados por objetos Field, filas por sus índices, y los valores son objetos, casi siempre Cuerdas
  • Algunas columnas tendrán una gran cantidad de valores repetidos
  • operaciones primarias son actualizar o eliminar registros basados ​​en valores de ciertos campos, y también agregar/eliminar/combinar columnas

Conozco opciones como H2 y Derby, pero en este caso no estoy buscando utilizar una base de datos incrustada.

EDIT: Si está sugiriendo bibliotecas, también agradecería que me indicara una o dos clases particulares que se aplicarían aquí. Mientras que la documentación de Sun generalmente incluye información sobre qué operaciones son O (1), que son O (N), etc., no veo mucho de eso en bibliotecas de terceros, ni realmente ninguna descripción de qué clases son las más adecuadas para qué .

+3

Aquí hay una herramienta para ayudarlo a comparar la huella de memoria de cualquier estructura que elija: http://code.google.com/p/memory-measurer/, y vea algunos datos de ejemplo que obtuve de ella: http://code.google.com/p/memory-measurer/wiki/ElementCostInDataStructures –

+0

Los enlaces anteriores obtuvieron brocken –

Respuesta

3

Supongo que tiene un mapa de Map<ColumnName,Column>, donde la columna es realmente algo así como ArrayList<Object>.

algunas posibilidades -

  • ¿Estás completamente seguro de que la memoria es un problema? Si solo te preocupa el tamaño, valdría la pena confirmar que esto realmente será un problema en un programa en ejecución. Se necesitan muchísimas filas y mapas para completar una JVM.

  • Puede probar su conjunto de datos con diferentes tipos de mapas en las colecciones. Dependiendo de sus datos, también puede inicializar mapas con combinaciones predeterminadas de tamaño/factor de carga que pueden ayudar. Me he equivocado con esto en el pasado, es posible que obtengas una reducción del 30% en la memoria si tienes suerte.

  • ¿Qué hay de almacenar sus datos en una única estructura de datos parecida a una matriz (una implementación de biblioteca existente o algo así como un envoltorio alrededor de una Lista de Listas), con un solo mapa que asigna claves de columna a columnas de matriz?

+0

En realidad cada registro es un Mapa qué Objeto son los valores de cada campo. Todos los registros están contenidos en una ArrayList. La memoria es definitivamente un problema. Cargar un archivo de 50MB a veces puede exceder 1GB de memoria, lo que me lleva a pensar que mi implementación actual es terriblemente ingenua. –

+0

Haré algunas pruebas con diferentes opciones; Lo que trato de hacer aquí es restringir el campo a unas pocas clases específicas dentro de diferentes bibliotecas que puedo comparar. –

+0

@bemace: ¿Está reutilizando los mismos objetos de campo para cada instancia de mapa de registro? –

11

Algunas columnas tendrán una gran cantidad de valores repetidos

sugiere inmediatamente a mí el posible uso de la FlyWeight pattern, independientemente de la solución que elija para sus colecciones.

+1

Si bien no solucioné el problema principal, esto me impulsó a finalmente descubrir cómo usar Cuerdas flyweight en java correctamente. Gracias. http://stackoverflow.com/questions/3972841/when-is-it-beneficial-to-flyweight-strings-in-java –

4

colecciones Trove deben tener un cuidado especial sobre el espacio ocupado (creo que también se han adaptado las estructuras de datos si se adhieren a los tipos primitivos) .. echar un vistazo here.

De lo contrario, puede probar con Apache collections .. ¡simplemente hagan sus puntos de referencia!

En anycase, si tiene muchas referencias en torno a los elementos mismos tratan de diseñar un patrón adecuado (como flyweight)

+0

Trove no funcionará para mí porque no estoy utilizando elementos primitivos. Veo que HashedMap en las colecciones de Apache es una "alternativa de propósito general", pero no dan ninguna explicación de lo que es diferente de HashMap.¿Tienes alguna idea allí? –

+0

En realidad, veo que menciona la adición de funcionalidad de iteración. Sin embargo, mi problema es con el rendimiento y las funciones que no faltan. –

1

mantiene sus datos en un ArrayList de HashMaps
Bueno, esta parte parece terriblemente ineficiente para mí. HashMap vacío ya asignará 16 * size of a pointer bytes (16 representa la capacidad inicial predeterminada), más algunas variables para el objeto hash (14 + psize). Si tienes muchas filas escasamente llenas, esto podría ser un gran problema.

Una opción sería usar un único hash grande con una clave compuesta (combinación de fila y columna). Aunque eso no hace que las operaciones en filas completas sean muy efectivas.

Además, dado que no menciona la operación de agregar celda, puede crear hashes con solo el almacenamiento interno necesario (parámetro initialCapacity).

No sé mucho sobre las colecciones de google, así que no puedo ayudar. Además, si encuentra alguna optimización útil, ¡por favor publique aquí! Sería interesante saber

+0

Te aseguro que * es * terriblemente ineficiente, por eso estoy aquí :) En mi caso, las filas dispersas no son un gran problema. –

0

Desde su descripción, parece que en lugar de un ArrayList de HashMaps que más bien desea un HashMap (Vinculado) de ArrayList (cada ArrayList sería una columna).

Agregaría un doble mapa desde el nombre de campo al número de columna, y algunos captadores/definidores inteligentes que nunca lanzan IndexOutOfBoundsException.

También puede usar un ArrayList<ArrayList<Object>> (básicamente una matriz dentada creciente dinámicamente) y mantener el mapeo fuera de los nombres de campo (columna).

Algunas columnas tendrán una gran cantidad de valores repetidos

dudo esto es importante, especialmente si son Cuerdas, (que son internalizadas) y su colección almacenarían referencias a ellos.

2

Guava incluye una interfaz Table y una implementación basada en hash. Parece un ajuste natural a su problema. Tenga en cuenta que esto aún está marcado como beta.

+4

Las implementaciones de la tabla Guava se implementan como un mapa con valores de mapa. Como resultado, no reducirán el uso de memoria. –

+0

@Jared Supongo que eso dependerá de la implementación de Map used? –

+0

@Jared, tienes razón. – whiskeysierra

3

Suponiendo que todas sus filas tienen la mayoría de las mismas columnas, puede usar una matriz para cada fila y un Map < ColumnKey, Integer> para buscar qué columnas se refieren a qué celda. De esta forma, solo tiene 4-8 bytes de sobrecarga por celda.

Si a menudo se repiten las cadenas, puede usar un grupo de cadenas para reducir la duplicación de cadenas. Los grupos de objetos para otros tipos inmutables pueden ser útiles para reducir la memoria consumida.

EDITAR: Puede estructurar sus datos como basados ​​en filas o en columnas.Si se basa en sus filas (una matriz de celdas por fila) agregar/eliminar la fila es solo cuestión de eliminar esta fila. Si está basado en columnas, puede tener una matriz por columna. Esto puede hacer que el manejo de tipos primitivos sea mucho más eficiente. es decir, puede tener una columna que sea int [] y otra que sea doble [], es mucho más común que una columna entera tenga el mismo tipo de datos, en lugar de tener el mismo tipo de datos para una fila completa.

Sin embargo, en cualquiera de los modos en que se estructuran los datos, se utilizará para la modificación de filas o columnas y la realización de un agregar/eliminar del otro tipo dará como resultado la reconstrucción de todo el conjunto de datos.

(Algo que hago es tener datos basados ​​en filas y agregar columnas al final, suponiendo que una fila no es lo suficientemente larga, la columna tiene un valor predeterminado, esto evita una reconstrucción al agregar una columna. columna, tengo un medio de ignorarlo)

+2

Si los valores del póster original son realmente densos, esto funcionará muy bien. Objeto [] [] o lista . ¡No descartes los viejos comentarios! Agrega el campo # getNumber() y estás dorado. En cuanto a la duplicación de valores, la interfaz 'Interner' de guava-libraries parece ajustarse a la factura. –

+0

Sí, eso es lo que tenía en mente. –

+0

No es una mala idea, pero ¿cómo se maneja agregar y eliminar filas/columnas con ese tipo de estructura? –

1

He estado experimentando con el uso del SparseObjectMatrix2D del proyecto Colt. Mis datos son bastante densos, pero sus clases de Matrix realmente no ofrecen ninguna forma de ampliarlos, así que fui con un conjunto de matrices dispersas al tamaño máximo.

Parece que utiliza aproximadamente un 10% menos de memoria y carga aproximadamente un 15% más rápido para los mismos datos, además de ofrecer algunos métodos de manipulación inteligentes. Todavía interesado en otras opciones sin embargo.

0

¿Por qué no intentas usar la implementación de la memoria caché como EHCache. Esto resultó ser muy efectivo para mí, cuando llegué a la misma situación.
Puede simplemente almacenar su colección dentro de la implementación de EHcache. Hay configuraciones como:

Maximum bytes to be used from Local heap. 

Una vez que los bytes utilizados por los desbordamientos de aplicaciones que configuran en la memoria caché, a continuación, la aplicación de caché se encarga de escribir los datos en el disco. También puede configurar la cantidad de tiempo después de la cual los objetos se escriben en el disco usando el algoritmo Usado Menos Reciente. Puede estar seguro de evitar cualquier error de falta de memoria, utilizando este tipo de implementaciones de caché. Solo aumenta las operaciones de E/S de su aplicación en un pequeño grado.
Esto es solo una vista de pájaro de la configuración. Hay muchas configuraciones para optimizar sus requisitos.

1

Chronicle Map podría tener una sobrecarga de menos de 20 bytes por entrada (ver a test demostrando esto). Para la comparación, la sobrecarga de java.util.HashMap varía de 37-42 bytes con -XX:+UseCompressedOops a 58-69 bytes sin ups comprimidos (reference).

Además, Chronicle Map almacena las claves y los valores fuera del montón, por lo que no almacena los encabezados de objetos, que no se contabilizan como la sobrecarga de HashMap anterior. Chronicle Map integrates con Chronicle-Values, una biblioteca para la generación de implementaciones flyweight de interfaces, el patrón suggested by Brian Agnew en otra respuesta.

Cuestiones relacionadas