2010-03-06 18 views
13

Tengo un mapa que utiliza un conjunto para el tipo de clave, de esta manera:del uso de conjuntos como claves en correlaciones Java

Map<Set<Thing>, Val> map; 

Cuando consulto map.containsKey (myBunchOfThings), devuelve falso, y No entiendo por qué. Puedo iterar a través de cada tecla en el conjunto de claves y verificar que haya una clave que (1) tenga el mismo código hash y (2) que sea igual a() myBunchOfThings.

System.out.println(map.containsKey(myBunchOfThings)); // false. 
for (Set<Thing> k : map.keySet()) { 
    if (k.hashCode() == myBunchOfThings.hashCode() && k.equals(myBunchOfThings) { 
    System.out.println("Fail at life."); // it prints this. 
    } 
} 

¿No entiendo básicamente el contrato de containsKey? ¿Existe un secreto para usar conjuntos (o más generalmente, colecciones) como claves para los mapas?

Respuesta

19

La clave no se debe mutar mientras se usa en el mapa. El documento Map java dice:

Nota: el gran cuidado debe tener cuidado si se utilizan objetos mutables como teclas de mapas. El comportamiento de un mapa no se especifica si el valor de un objeto se cambia de una manera que afecta a las comparaciones de mientras el objeto es una clave en el mapa. Un caso especial de esta prohibición es que no es admisible que un mapa contenga como una clave. Si bien es admisible que un mapa contenga en sí como valor, se recomienda extrema precaución: recomendado: los métodos equal y hashCode ya no están bien definidos en tal mapa.

Conocí este problema, pero nunca hice la prueba hasta ahora. Elaboro luego un poco más:

Map<Set<String>, Object> map = new HashMap<Set<String>, Object>(); 

    Set<String> key1 = new HashSet<String>(); 
    key1.add("hello"); 

    Set<String> key2 = new HashSet<String>(); 
    key2.add("hello2"); 

    Set<String> key2clone = new HashSet<String>(); 
    key2clone.add("hello2"); 

    map.put(key1, new Object()); 
    map.put(key2, new Object()); 

    System.out.println(map.containsKey(key1)); // true 
    System.out.println(map.containsKey(key2)); // true 
    System.out.println(map.containsKey(key2clone)); // true 

    key2.add("mutate"); 

    System.out.println(map.containsKey(key1)); // true 
    System.out.println(map.containsKey(key2)); // false 
    System.out.println(map.containsKey(key2clone)); // false (*) 

    key2.remove("mutate"); 

    System.out.println(map.containsKey(key1)); // true 
    System.out.println(map.containsKey(key2)); // true 
    System.out.println(map.containsKey(key2clone)); // true 

Después key2 está mutado, el mapa no contiene más. Podríamos pensar que el mapa "indexa" los datos cuando se agregan y que entonces esperamos que aún contengan el clon key2 (línea marcada con *). Pero gracioso, este no es el caso.

Así que, como dice el documento de Java, las claves no deberían estar mutadas, de lo contrario, el comportamiento es no especificado. Período.

Supongo que eso es lo que sucede en su caso.

+0

Tiene toda la razón sobre el tema _unspecified_. Supongo que tendré que implementar algún tipo de estructura de búsqueda similar a un árbol, donde los nodos tienen valores tanto como los hijos. –

2

¿Modificó el conjunto después de la inserción? Si es así, es posible que el conjunto se clasifique en un cubo diferente al que está buscando. Al iterar, encuentra su conjunto, porque se ve en todo el mapa.

Creo que el contrato para HashMap afirma que no está autorizado a modificar el código hash para los objetos que se utilizan como clave,

0

¿Está eliminando el conjunto exacto (el conjunto que desea encontrar) cuando se comparan para la clave ?

+0

probablemente no, pero eso no importa. – Jorn

6

Debe esforzarse por utilizar tipos inmutables como claves para Map s. Las colecciones y los conjuntos generalmente son muy fáciles de cambiar, por lo que, por lo general, es una mala idea usarlos de esta manera.

Si desea utilizar muchos valores clave como una clave Map, debe utilizar una implementación de clase diseñada para tal fin, como Apache Commons Collections MultiKey.

Si realmente debe utilizar un Conjunto o Colección como clave, primero hágalo inmutable (Collections.unmodifiableSet(...)) y luego no mantenga una referencia al objeto de respaldo mutable.

Otra dificultad con el uso de Colecciones como claves es que se pueden construir en un orden diferente. Solo una colección ordenada tendrá una probabilidad alta de emparejamiento. Por ejemplo, si usa una orden ordenada secuencialmente ArrayList pero construye la lista de una manera diferente la segunda vez no coincidirá con la clave; el código hash y el orden de los valores es diferente.

EDIT: Estoy corregido en esta declaración a continuación, nunca he tenido que usar Establecer para un ket. Acabo de leer una parte de la implementación de hashCode en AbstractHashSet. Esto usa un total simple de todos los valores, por lo que no depende del orden. Igual también comprueba que un conjunto contiene todos los valores en el otro conjunto. Sin embargo, esto sigue siendo cierto con otros tipos de colecciones en Java (el orden de ArrayList sí importa).

Si su colección es realmente HashSet, la orden de creación también puede ser importante. De hecho, una colección gestionada por hash de cualquier tipo será aún más problemática ya que cualquier cambio en la capacidad desencadenará una reconstrucción de toda la colección que puede reordenar los elementos. Piense en colisiones de hashes que se almacenan en el orden en que ocurre la colisión (una cadena simple enlazada de todos los elementos donde el valor hash transformado es el mismo).

+1

Según el documento de Java, dos conjuntos son iguales si tienen los mismos elementos, sin importar el orden. Incluso para 'SortedSet'. 'SortedSet' cambia el recorrido del conjunto con el iterador pero no la igualdad. El orden de los elementos debe considerarse solo en List. ¿Está seguro de su comentario sobre 'HashSet'? – ewernli

+0

"cualquier cambio de capacidad desencadena una reconstrucción de toda la colección que puede reordenar los elementos" Eso es cierto, pero creo que ewernli tiene razón en que los métodos equals() y hashCode() están escritos para que tales diferencias no causen dos conjuntos iguales para comparar como desigual simplemente porque fueron construidos de manera diferente. – MatrixFrog

+0

Voy a rehuir las cosas de Apache Collections, ya que parece que no se mantiene activamente. Al menos, no parece que sea compatible con los genéricos, lo que hace que mi neurona se "aleje". –

Cuestiones relacionadas