2010-08-31 22 views
7

Tengo un mapa grande de String-> Integer y quiero encontrar los 5 valores más altos en el mapa. Mi enfoque actual implica traducir el mapa en una lista de matrices de objetos de par (clave, valor) y luego ordenar usando Collections.sort() antes de tomar los primeros 5. Es posible que una clave tenga su valor actualizado durante el curso de la operación .Encontrar los valores más altos de n en un mapa

Creo que este enfoque es aceptable con una sola hebra, pero si tuviera múltiples hilos todos activando la transposición y clasificación con frecuencia, no parece muy eficiente. La alternativa parece ser mantener una lista separada de las 5 entradas más altas y mantenerla actualizada cuando se realizan operaciones relevantes en el mapa.

¿Podría obtener algunas sugerencias/alternativas para optimizar esto, por favor? Me complace considerar diferentes estructuras de datos si hay un beneficio.

Gracias!

+0

Dos preguntas: 1) ¿por qué tener un mapa? ¿necesita buscar valores para claves dadas? 2) ¿También necesita saber las claves para los 5 valores más altos? – pgras

+0

@pgras: sí, otra función de la API es recibir una clave y devolver el valor actual para que el mapa sea un buen punto de partida. Necesitamos saber las claves para los valores más altos, por lo que me vi obligado a usar un objeto de par y no solo crear una lista de enteros. – Scruffers

+0

¿Puede especificar qué requisitos de tiempo de ejecución tiene exactamente en mente? Su 'getHighestFive' ​​actual es' O (n log n) ', mientras que cambiar el mapa con' lookup', 'insert' y' delete' es 'O (log n)' cada uno. ¿Desea bajar 'getHighestFive' ​​a' O (1) 'mientras conserva los otros tiempos de ejecución? ¿Qué tiene que ver esto con múltiples hilos, quieres paralelizar 'getHighestFive'? –

Respuesta

2

creo que este enfoque es el único subproceso aceptable, pero si tuviera múltiples hilos de toda la transpuesta de activación y clasificar con frecuencia no parece muy eficiente. La alternativa parece ser mantener una lista separada de las 5 entradas más altas y mantenerla actualizada cuando se realizan operaciones relevantes en el mapa.

Hay un enfoque intermedio que puede tomar también. Cuando un hilo solicita una "vista ordenada" del mapa, crea una copia del mapa y luego maneja la clasificación en ese mapa.

public List<Integer> getMaxFive() { 
    Map<String, Integer> copy = null; 
    synchronized(lockObject) { 
     copy = new HashMap<String, Integer>(originalMap); 
    } 

    //sort the copy as usual 
    return list; 
} 

ideal sería que si usted tiene algún estado (como este mapa) se accede por múltiples hilos, que está encapsulando el estado detrás de alguna otra clase de manera que cada hilo no se actualiza el mapa directamente.

5

Bueno, para encontrar los 5 valores más altos en un Mapa, puede hacerlo en el momento O(n) donde cualquier tipo es más lento que eso.

La manera más fácil es simplemente hacer un ciclo for en el conjunto de entradas del Mapa.

for (Entry<String, Integer> entry: map.entrySet()) { 
    if (entry.getValue() > smallestMaxSoFar) 
     updateListOfMaximums(); 
} 
0

Intente con otra estructura de datos. Supongamos que hay una clase llamada MyClass cuyos atributos son key (String) y value (int). MyClass, por supuesto, necesita implementar una interfaz comparable. Otro enfoque es crear una clase llamada MyClassComparator que amplíe Comparator.

El método compareTo (independientemente de dónde se encuentre) debe definirse así: compareTo (parámetros) { return value2 - value1; // descendiendo }

El resto es fácil. Usar el método List e invocar Collections.sort (parameters) hará la parte de clasificación.

No sé qué algoritmo de clasificación usa Collections.sort (parameters). Pero si crees que algunos datos pueden aparecer con el tiempo, necesitarás un tipo de inserción. Dado que es bueno para una información casi ordenada, es online.

+0

Otra función de la API tiene la necesidad de recuperar una clave rápidamente, por lo que el intercambio de una Colección en lugar de un Mapa perjudicaría ese rendimiento inaceptablemente ya que la lista es grande. Sin embargo, su idea es sólida: no hay ninguna razón por la que no pueda mapear (clave -> compuesto (clave, valor)) donde los implementos compuestos sean comparables. Entonces podría simplemente decir Collections.sort (map.values ​​()). Desafortunadamente, esto todavía tiene el impacto en el rendimiento cuando se introducen varios hilos, ya que cada hilo podría combinarse (O (n log n)). – Scruffers

3

se pueden utilizar dos mapas:

// Map name to value 
Map<String, Integer> byName 

// Maps value to names 
NavigableMap<Integer, Collection<String>> byValue 

y asegúrese de mantener siempre en sincronía (posiblemente envuelva tanto en otra clase que se encarga de poner, obtener, etc). Para los valores más altos, use byValue.navigableKeySet().descendingIterator().

+0

Me gusta mucho esto, pero desde la memoria no haría esto requerir que todos los valores sean únicos. Es poco probable que este sea el caso en mi dominio, por lo que es probable que el mapa byValue se corrompa. – Scruffers

+0

Buen punto, modifiqué 'byValue' para contener todos los nombres para un valor dado. –

0

Si las modificaciones son raras, implementaría algunas SortedByValHashMap<K,V> extends HashMap <K,V>, similar a LinkedHashMap) que mantiene las entradas ordenadas por valor.

1

me gustaría crear un método como:

private static int[] getMaxFromMap(Map<String, Integer> map, int qty) { 
    int[] max = new int[qty]; 
    for (int a=0; a<qty; a++) { 
     max[a] = Collections.max(map.values()); 
     map.values().removeAll(Collections.singleton(max[a])); 
     if (map.size() == 0) 
      break; 
    } 
    return max; 
} 

Aprovechando Collections.max() y Collections.singleton()

+1

Esto es O (n), pero en la práctica funciona bastante despacio en comparación con otros métodos. – Kru

1

Hay dos maneras de hacerlo fácilmente:

  1. poner el mapa en un heap structure y recupera los elementos n que quieras de él.
  2. Iterar a través del mapa y actualizar una lista de n valores más altos con cada entrada.

Si desea recuperar un número desconocido o un gran número de valores más altos, el primer método es el camino a seguir. Si tiene una pequeña cantidad fija de valores para recuperar, la segunda podría ser más fácil de entender para algunos programadores. Personalmente, prefiero el primer método.

Cuestiones relacionadas