La forma más rápida de descargar un mapa hash en el disco en un conjunto ordenado

Tengo un Map<byte[], Element> y quiero ordenarlo y escribirlo en el disco, de modo que tenga un archivo con todos los elementos ordenados por clave a través de Guava's UnsignedBytes.lexicographicalComparator.

Lo que estoy haciendo ahora mismo es:

HashMap<byte[], Element> memory;

// ... code creating and populating memory ...

TreeMap<byte[], Element> sortedMap = new TreeMap<byte[], Element>(UnsignedBytes.lexicographicalComparator());
sortedMap.putAll(memory.getMap());

MyWriter writer = new MyWriter("myfile.dat");
for (Element element: sortedMap.values())
    writer.write(element);
writer.close();

Probablemente sea difícil hacer la clasificación más rápida (O (nlogn)), la pregunta es si puedo mejorar la navegación de la lista ordenada. Lo ideal sería clasificar en un ArrayList en lugar de un TreeMap, por lo que iterar a través de él sería muy rápido.

Pensé en poner el HashMap en un ArrayList y Collections.sort() , pero eso requeriría más copias que la solución real.

¿Alguna idea?

Edit:

Añado aquí mi prueba con ArrayList que es 2 veces más rápido, pero supongo que usa más memoria. ¿Quizás algunos comentarios sobre esta suposición?

// ArrayList-based implementation 2x faster
ArrayList<Element> sorted = new ArrayList<Element>(memory.size());
sorted.addAll(memory.values());

final Comparator<byte[]> lexic = UnsignedBytes.lexicographicalComparator();

Collections.sort(sorted, new Comparator<Element>(){
    public int compare(Element arg0, Element arg1) {
        return lexic.compare(arg0.getKey(), arg1.getKey());
    }
});
MyWriter writer = new MyWriter(filename);

for (Element element: sorted)
    writer.write(element);
writer.close();

preguntado el 16 de mayo de 11 a las 17:05

Lo principal que necesita mejorar es la escritura en disco. Esto puede ser 100 veces más lento que cualquier otra cosa que hagas. Usaría un generador de perfiles para verificar dónde está pasando su tiempo. -

hay pocas mejoras allí, ya estoy usando DataOutputStream con Buffering, es un enfoque secuencial sencillo. Como muestra mi microbenchmark, la clasificación y la iteración marcan la diferencia. -

¿Por qué dices hacer una ArrayList > (por ejemplo) y ordenarlo requiere más 'copiar' que construir un TreeMap? -

"El algoritmo de clasificación es un mergesort modificado (en el que se omite la combinación si el elemento más alto de la sublista baja es menor que el elemento más bajo de la sublista alta). Este algoritmo ofrece un rendimiento n log (n) garantizado. Esta implementación vuelca el lista especificada en una matriz, ordena la matriz e itera sobre la lista restableciendo cada elemento desde la posición correspondiente en la matriz. Esto evita el rendimiento n2 log (n) que resultaría de intentar ordenar una lista vinculada en su lugar ". Entonces los elementos se copian en una matriz. -

1 Respuestas

Tu pregunta fue "¿Alguna idea?". Supongo que cualquier cosa que pudiera escribir sería una respuesta.

Tuve el mismo problema que usted, y comparé ampliamente las dos soluciones: use un mapa de árbol para que los elementos se clasifiquen por adelantado o ordénelos después de los hechos. Mi punto de referencia mostró el mismo resultado que el suyo. Es más rápido ordenar después del hecho.

No me preocuparía el hecho de que el segundo enfoque requiera más copias. Primero, más rápido es más rápido, ¿verdad? Si el segundo enfoque requiere menos ciclos de CPU, entonces es mejor.

Si la memoria es un problema, tenga en cuenta que los mapas de árbol y los mapas hash ocupan mucha más memoria por elemento que un ArrayList, que está respaldado por una matriz de objetos simple. Cada elemento de un mapa de árbol o hashmap requiere al menos un objeto y, por lo general, más. Los objetos tienen mucha sobrecarga, 32 o más bytes. En una matriz plana, cada elemento ocupa solo 4 bytes.

Mis puntos de referencia mostraron que el tiempo para asignar una matriz desde la memoria era aproximadamente proporcional al tamaño de la matriz, una vez que llegaba a un tamaño de matriz de unas pocas docenas de bytes. Por lo tanto, la asignación de ArrayList puede ser lenta si es realmente grande. Aún así, creo que es la mejor opción, siempre que no haya peligro de quedarse sin memoria.

contestado el 16 de mayo de 11 a las 23:05

en realidad, estaba verificando el consumo de memoria con visualvm y la implementación del mapa de árbol usa más memoria. Supongo que como tengo alrededor de 5 millones de elementos, eso equivale a unos 20 MB para la matriz. Eso está muy bien para mí. ¡Gracias por tu conocimiento! - Marcorossi

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.