2010-09-16 18 views
8

Existen algunos casos en que los objetos clave utilizados en el mapa no anulan hashCode() e iguales a() de Object, para ejemplos, use un socket Connection o java.lang.Class como claves.Java HashMap o IdentityHashMap

  1. ¿Existe algún defecto potencial para utilizar estos objetos como claves en un HashMap?
  2. ¿Debo usar IdentityHashMap en estos casos?

Respuesta

5

Si equals() y hashCode() no se anulan en los objetos clave, HashMap e IdentityHashMap deben tener la misma semántica. La implementación predeterminada equals() usa semántica de referencia, y el valor predeterminado hashCode() es el código hash de identidad del sistema del objeto.

Esto solo es perjudicial en los casos en que diferentes instancias de un objeto se pueden considerar lógicamente iguales. Por ejemplo, usted no desea utilizar IdentityHashMap si sus claves fueron:

new Integer(1) 

y

new Integer(1) 

ya que estos son técnicamente diferentes instancias de la clase Integer. (Usted realmente debe utilizar Integer.valueOf(1), pero que está poniendo fuera de tema.)

Class como claves deben estar bien, excepto en circunstancias muy especiales (por ejemplo, la biblioteca Hibernate ORM genera subclases de las clases en tiempo de ejecución con el fin de implementar un proxy.) Como desarrollador, sería escéptico con respecto al código que almacena los objetos Connection en un mapa como claves (¿quizás debería usar un grupo de conexiones, si está administrando conexiones de bases de datos?). Que funcionen o no depende de la implementación (ya que Connection es solo una interfaz).

Además, es importante tener en cuenta que HashMap espera que la determinación equals() y hashCode() permanezca constante. En particular, si implementa algún hashCode() personalizado que utiliza campos mutables en el objeto clave, cambiar un campo clave puede hacer que la clave se 'pierda' en el cubo de hashtable incorrecto del HashMap. En estos casos, es posible que pueda usar IdentityHashMap (dependiendo del objeto y su caso de uso particular), o simplemente podría necesitar una implementación diferente de equals()/hashCode().

0

Hasta donde yo sé, el único problema con un hashmap con claves erróneas sería con hasmaps muy grandes, sus claves podrían ser muy malas y obtendrá o (n) tiempo de recuperación, en lugar de o (1) Si se rompe algo más, me interesaría saberlo :)

+1

te refieres a los malos métodos hashCode ¿no es así? –

1

En la situación que describe, los comportamientos de HashMap y IdentityHashMap son idénticos.

En el contraste con esto, si las teclas anulan equals() y hashCode(), los comportamientos de dos mapas son diferentes.

ver el javadoc de java.util.IdentityHashMap a continuación.

Esta clase implementa la interfaz de Mapa con una tabla hash, utilizando reference-equality en lugar de object-equality al comparar claves (y valores). En otras palabras, en IdentityHashMap, dos claves k1 y k2 se consideran iguales si y solo si (k1 == k2). (En las implementaciones de mapas normales (como HashMap) dos claves k1 y k2 se consideran iguales si y solo si (k1 == nulo? K2 == nulo: k1.equals (k2)).)

En resumen, mi respuesta es que:

  1. ¿Hay cualquier defecto potencial para usar como teclas de estos objetos en un HashMap? ->No
  2. ¿Debo usar IdentityHashMap en estos casos? -> No se
1

Si bien no hay problema teórico, se debe evitar IdentityHashMap a menos que tenga una razón explícita para usarlo. No proporciona ningún rendimiento apreciable u otro beneficio en el caso general, y cuando inevitablemente comiences a introducir objetos en el mapa que hacen anula equals() y hashCode(), acabarás teniendo errores sutiles y difíciles de diagnosticar.

Si cree que necesita IdentityHashMap por motivos de rendimiento, utilice un generador de perfiles para confirmar esa sospecha antes de realizar el cambio. Supongo que encontrará muchas otras oportunidades de optimización que son más seguras y marcan una gran diferencia.

+0

Nitpick - en el caso general '==' y 'System.identityHashCode' ** son ** más rápidos que los típicos métodos sobrecargados' equals' y 'hashCode'. Pero estoy de acuerdo en que esa no es una buena razón para usar 'IdentityHashMap' en lugar de' HashMap'. –

+0

@Stephen C: es suficiente, he agregado un descargo de responsabilidad. :) –

3

Desde el punto de vista de seguridad de un código de móvil, existen situaciones en las que es necesario usar IdentityHashMap o similar. Las implementaciones maliciosas de clases de clave que no sean final pueden anular hashCode y equals para que sean maliciosas. Pueden, por ejemplo, reclamar la igualdad ante diferentes instancias, adquirir una referencia a otras instancias con las que se comparan, etc. Sugiero romper con la práctica estándar manteniéndote seguro y usando IdentityHashMap donde quieras semántica de identidad. Raramente hay una buena razón para cambiar el significado de igualdad en una subclase donde la superclase ya se está comparando. Supongo que el escenario más probable es un proxy roto no simétrico.

La implementación de IdentityHashMap es bastante diferente de HashMap. Utiliza sondeos lineales en lugar de objetos Entry como enlaces en una cadena. Esto lleva a una ligera reducción en el número de objetos, aunque una pequeña diferencia en el uso total de la memoria. No tengo buenas estadísticas de rendimiento que pueda citar. Solía ​​haber una diferencia de rendimiento entre el uso (no reemplazado) Object.hashCode y System.identityHashCode, pero eso fue aclarado hace unos años.