La pregunta es (ahora) sobre el almacenamiento de muchos datos, que se pueden representar usando tipos primitivos como int
, en un Mapa. Algunas de las respuestas aquí son muy engañosas en mi opinión. A ver por qué.
Modifiqué el punto de referencia de tesoro para medir tanto el tiempo de ejecución como el consumo de memoria. También agregué PCJ a este punto de referencia, que es otra biblioteca de colecciones para tipos primitivos (lo uso ampliamente). El punto de referencia del tesoro 'oficial' no compara IntIntMaps con los de la Colección Java Map<Integer, Integer>
, probablemente almacenar Integers
y almacenar ints
no es lo mismo desde un punto de vista técnico. Pero a un usuario puede no importarle este detalle técnico, quiere almacenar datos representables de manera ints
eficiente.
Primero la parte relevante del código:
new Operation() {
private long usedMem() {
System.gc();
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
// trove
public void ours() {
long mem = usedMem();
TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
ours.put(i, i);
}
mem = usedMem() - mem;
System.err.println("trove " + mem + " bytes");
ours.clear();
}
public void pcj() {
long mem = usedMem();
IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("pcj " + mem + " bytes");
map.clear();
}
// java collections
public void theirs() {
long mem = usedMem();
Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("java " + mem + " bytes");
map.clear();
}
Supongo que los datos son primitivos ints
, lo que parece cuerdo. Pero esto implica una penalización de tiempo de ejecución para java util, debido al auto-boxeo, que no es necesario para los marcos de colecciones primitivas.
Los resultados de tiempo de ejecución (sin gc()
llamadas, por supuesto) en WinXP, jdk1.6.0_10:
100000 operaciones de colocación 100000 contiene operaciones
colecciones java 1938 ms 203 ms
tesoro 234 ms 125 ms
pcj 516 ms 94 ms
Si bien esto puede parecer drástico, esta no es la razón para usar dicho marco.
La razón es el rendimiento de la memoria. Los resultados para un mapa que contiene 100000 int
entradas:
Las colecciones java oscilan entre 6644536 y 7168840 bytes
trove 1853296 bytes
pcj 1866112 bytes
Java Collections necesita más de tres veces la memoria en comparación con los marcos de colección primitivos. Es decir, puede mantener tres veces más datos en la memoria, sin recurrir al disco IO que reduce el rendimiento del tiempo de ejecución por magnitud. Y esto importa. Lea la escalabilidad alta para descubrir por qué.
En mi experiencia, el alto consumo de memoria es el mayor problema de rendimiento con Java, que por supuesto también resulta en un peor rendimiento en tiempo de ejecución. Los marcos de colección primitivos realmente pueden ayudar aquí.
Entonces: No, java.util no es la respuesta. Y "agregar funcionalidad" a las colecciones de Java no es el punto cuando se pregunta sobre la eficiencia. Además, las colecciones modernas de JDK no "superan incluso a las colecciones especializadas de Trove".
Descargo de responsabilidad: el punto de referencia aquí está lejos de ser completo, ni es perfecto. Está destinado a llevar a casa el punto, que he experimentado en muchos proyectos. Las colecciones primitivas son lo suficientemente útiles como para tolerar la API sospechosa, si trabaja con muchos datos.