2011-11-17 14 views
8

Necesito un mapa para hacer una caché en Java para los mismos valores que tengo dos claves de cadena. Mi pregunta es mejor hacer mapas anidados (uno para cada clave) o crear algún tipo de clave personalizada con las dos cadenas.Mapas anidados o claves combinadas en java

El acceso a los datos en caché se accederá en todo momento con las dos teclas y no es necesario agruparlo con ninguna de esas dos teclas.

Entonces, si es mejor combinar la clave de cadena en una sola, ¿qué es mejor?

  • clase personalizada con método personalizado getHash. Pero entonces, ¿cuál es el problema que implementa la función hash?
  • Simplemente concatene dos cadenas juntas. Por ejemplo:

    cache.put (key1 + clave2, valor)

Respuesta

10

Puede hacer mapas anidados o utilizar una clase personalizada que defina hashCode().

Por lo general no es una buena idea para concatenar las teclas, puede terminar con las colisiones, como en el caso de llaves 1 y 22 y claves 12 y 2. Se asignarían al mismo valor 122.

Si siempre va a utilizar las dos teclas, utilizando un único Map siempre habrá un poco más eficiente, y siempre se puede definir su propio adaptador para el mapa que se llevará a dos argumentos:

public class MyCache { 

    private Map<MyKey, Object> cache = new HashMap<MyKey, Object>(); 

    public Object getObject(Object key1, Object key2){ 
     return cache.get(new MyKey(key1, key2)); 
    } 
    public void putObject(Object key1, Object key2, Object value){ 
     cache.put(new MyKey(key1, key2), value); 
    } 
} 

Recuerde para definir equals() y hashCode() en su clase de clave personalizada (agregue cheques para la nulidad si es necesario).

public int hashCode() { 
    int result = 17; 
    result = 37 * result + keyA.hashCode(); 
    result = 37 * result + keyB.hashCode(); 
    return result; 
} 

public boolean equals(Object another) { 
    return another.keyA.equals(keyA) && another.keyB.equals(keyB); 
} 
2

El uso de llaves combinadas parece más lógico para mí, y más fácil de usar (que no tiene que preocuparse por el mapa anidada siendo instancied).

Acerca de la combinación de las teclas, utilice una clase personalizada. Tiene más sentido semánticamente y evita las colisiones si tiene cadenas similares. Dicha colisión podría aparecer si tiene la clave ["ab", "c"] y la clave ["a", "bc"].

Recuerde cuando escribe su clase personalizada, necesita escribir los métodos equals y hashCode correctamente, o su caché podría no funcionar (y también podría sufrir problemas de rendimiento).

3

es mejor hacer mapas anidados (uno para cada clave) o hacer algún tipo de clave personalizada marcada con las dos cadenas?

Una tabla hash única con un tipo de clave compuesta puede tener una mejor localidad de referencia. Es casi seguro que requerirá menos memoria y será un poco más rápido, aunque de cuánto depende su aplicación.

El diseño de una función hash personalizada puede ser complicado, pero puede comenzar con una función que solo toma los hashes ya definidos de los miembros de la clave compuesta y los XOR juntos. También puede tomar la ruta de cadena, pero luego asegúrese de concatenar los miembros con algún marcador entre ellos (como ":") que es poco probable que ocurra en ellos.

0

Creo que el uso de mapas anidados es menos óptimo. Estoy de acuerdo con su segundo pensamiento - implementar una clase personalizada con overriden hashCode

public int hashCode(){ 
    return str1+str2.hashCode(); 
} 


y equals

public boolean equals(Object o){ 
    //compare both keys here 
} 

Asimismo, no se olvide de reemplazar los mismos métodos en la clase del objeto almacenado.

Luego, en caso de [ "ab", "c"] tendrá el mismo código hash, pero diferentes iguales resultar

12

Es posible que desee considerar Table clases de guayaba. Implementan un Map con dos claves por valor. Esto puede proporcionar una solución más legible que una clave compuesta según sus requisitos.

Table<String, String, MyValue> 
+0

Muchas gracias, eso era exactamente lo que estaba buscando. Solo tropecé con los MultiMaps de Guava, que no fueron lo suficientemente cómodos para mí. ;) –

+0

@FlorianPilz NP, cheers –

+0

Una nota acerca de HashBasedTable of Guava, utiliza un enfoque de mapa anidado. – PhoneixS

1

Tuve un problema similar con un sistema de almacenamiento en caché que necesitaba utilizar parámetros de método para la generación de claves. Terminé escribiendo una clase contenedora similar a la respuesta de Xavi.

public final class ArrayWrapper 
{ 
    private final Object[] array; 

    public ArrayWrapper(final Object... array) 
    { 
     this.array = array; 
    } 

    public Object[] getArray() 
    { 
     return this.array; 
    } 

    public boolean equals(Object o) 
    { 
     if (o == null) return false; 
     if (o == this) return true; 
     if (o instanceof ArrayWrapper) 
     { 
      return Arrays.equals(this.array, ((ArrayWrapper)o).array); 
     } 
     return false; 
    } 

    private int hashCode(Object o) 
    { 
     if (o == null) return 0; 
     return o.hashCode(); 
    } 

    public int hashCode() 
    { 
     int sum = 17; 
     if (this.array != null) for(int i = 0;i<this.array.length;i++) 
     { 
      sum = 37 * sum + this.hashCode(this.array[i]); 
     } 
     return sum; 
    } 

    public String toString() 
    { 
     if (this.array != null) 
     { 
      return "Wrapper " + Arrays.toString(array); 
     } 
     else return "Wrapper []"; 
    } 
} 

Crea una nueva Contenedor y úsala como clave para el Mapa. También puede escribir un mapa personalizado y sobrecargar los métodos get y put para aceptar una matriz (para obtener también vararg) en lugar de tener que ajustarla manualmente.

Cuestiones relacionadas