2011-07-15 23 views
22

Necesito crear cosas de la guía telefónica. Contiene el número de nombre &. Ahora cuando escribo letras, se debe devolver la lista de coincidencias. Para el ejemplo que se muestra a continuación, cuando escribo H, se debe devolver una lista que contenga Harmer, Harris, Hawken, Hosler. Cuando escriba Ha, entonces la lista que contiene solo Harmer, Harris, Hawken debe ser devuelta.Búsqueda parcial en HashMap

Map<String, String> nameNum = new HashMap<String, String>(); 

    nameNum.put("Brown", "+1236389023"); 
    nameNum.put("Bob", "+1236389023"); 
    nameNum.put("Harmer", "+1236389023"); 
    nameNum.put("Harris", "+1236389023"); 
    nameNum.put("Hawken", "+1236389023"); 
    nameNum.put("Hosler", "+1236389023"); 

¿Alguna idea de cómo lograrlo? Gracias de antemano.

+0

¿Estás seguro de que el uso de un 'HashMap' en absoluto es una buena idea para algo como esto? Creo que una estructura de datos diferente podría ser mejor. –

+0

¿Está buscando solo la primera letra o está eliminando la lista mientras escribe? Por ejemplo, ¿una entrada de "Ha" elimina a "Hosler"? –

Respuesta

25

Sí, un HashMap no es la estructura de datos correcta para esto. Como dijo Bozho, un Trie sería el correcto.

Con las herramientas de a bordo de Java, un TreeMap (o cualquier SortedMap, en realidad) podrían utilizarse:

public <V> SortedMap<String, V> filterPrefix(SortedMap<String,V> baseMap, String prefix) { 
    if(prefix.length() > 0) { 
     char nextLetter = prefix.charAt(prefix.length() -1) + 1; 
     String end = prefix.substring(0, prefix.length()-1) + nextLetter; 
     return baseMap.subMap(prefix, end); 
    } 
    return baseMap; 
} 

La salida incluso sería solucionado por la clave.

Aquí un ejemplo de uso:

SortedMap<String, String> nameNum = new TreeMap<String, String>(); 
// put your phone numbers 

String prefix = ...; 
for(Map.Entry<String,String> entry : filterPrefix(nameNum, prefix).entrySet()) { 
    System.out.println(entry); 
} 

Si desea que su filtro de prefijo que no sea en función de las diferencias de casos, utilizar un comparador adecuado para su mapa (como un Collator con un ajuste de resistencia adecuada, o String.CASE_INSENSITIVE_ORDER) .

+2

+1 idea correcta; también considere los problemas en mayúsculas/minúsculas –

+0

@ Paŭlo Ebermann: ¿Por qué Trie, cómo ahorra espacio {http://stackoverflow.com/questions/8265476/trie-saves-space-but-how}? –

+0

Puedes usar el prefijo + "\ uffff" como end, también. – Tires

9

Esto requiere una estructura de datos Trie. Ver this question para implementaciones de java. Usé this one.

+0

Gracias Bozho su enlace fue útil! Pero son casi 3 años desde que se respondió esa pregunta. ¿Hay alguna solución mejor ahora, de la que puedas estar al tanto? –

+0

http://code.google.com/p/patricia-trie/ Lo usé – Bozho

0

Ponlo todo en un MultiMap (o simplemente almacena una lista como el valor en tu HashMap). Por "Brown", de la tienda:

"B"->["Brown"] 
"BR"->["Brown"] 
"BRO"->["Brown"] 

Si posteriormente añade "Bradley":

"B"->["Brown", "Bradley"] 
"BR"->["Brown", "Bradley"] 
"BRO"->["Brown"] 
"BRA"->["Bradley"] 

etc ...

luego tener otro mapa a otro "Brown" o "Bradley" al número de teléfono.

+0

Agregar y eliminar elementos de esta estructura de datos sería * muy * costoso. –

+0

Estoy de acuerdo. Pero ni siquiera sabemos cuán grande es su "tipo de agenda". Prefiero hacer algo simple primero y optimizarlo más tarde. Esto parece ser lo más simple de hacer. – dgrant

+0

Los accesos serían O (1) mientras que para los árboles sería log (n). ¿Y no es eso más importante si estás haciendo algo como autocompletar? ¿Y con qué frecuencia se está actualizando el conjunto de datos? Si los get son mucho más frecuentes que los sets, a quién le importa lo lento que es agregar/eliminar. Y agregar y eliminar aquí ni siquiera es tan malo, no creo. – dgrant

-1

Utilizar guava Multimap facilitará su solución.

La clave es la primera letra del nombre, el valor es un Collection que contiene todos los pares de nombre-teléfono cuyo nombre se inicia con la tecla (primera letra).

Ejemplo:

public void test(){ 
     //firstLetter -> list of name-phone pair 
     Multimap<String, Pair> mMap = ArrayListMultimap.create(); 

     put(mMap, "Brown", "+1236389023"); 
     put(mMap, "Bob", "+1236389023"); 
     put(mMap, "Harmer", "+1236389023"); 
     put(mMap, "Harris", "+1236389023"); 
     put(mMap, "Hawken", "+1236389023"); 
     put(mMap, "Hosler", "+1236389023"); 

     //Test 
     System.out.println(mMap.get("H")); 
    } 

    void put(Multimap<String, Pair> mMap, String name, String phone){ 
     mMap.put(name.substring(0,1), new Pair(name, phone)); 
    } 

    public static class Pair{ 
     String name; 
     String phone; 

     public Pair(String name, String phone) { 
     this.name = name; 
     this.phone = phone; 
     } 

     @Override 
     public String toString() { 
     return "Pair [name="+name+", phone="+phone+"]"; 
     } 

}