2008-09-24 19 views
5

En Java, tengo una subclase Vertex de la clase Java3D Point3f. Ahora Point3f calcula equals() según los valores de sus coordenadas, pero para mi clase Vertex quiero ser más estricto: dos vértices son iguales si son el mismo objeto. Hasta ahora, todo bien:¿Cómo se calcula el hashCode() desde la dirección del objeto?

class Vertex extends Point3f { 

    // ... 

    public boolean equals(Object other) { 
     return this == other; 
    } 
} 

Sé que esto viola el contrato de equals(), pero ya que sólo a comparar vértices a otros vértices esto no es un problema.

Ahora, para poder poner vértices en un HashMap, el método hashCode() debe devolver resultados consistentes con equals(). Actualmente lo hace, pero probablemente basa su valor de retorno en los campos del Point3f, y por lo tanto dará colisiones hash para diferentes objetos Vertex con las mismas coordenadas. Por lo tanto, me gustaría basar el hashCode() en la dirección del objeto, en lugar de calcularlo desde los campos Vertex. Sé que la clase Object hace esto, pero no puedo llamar a su método hashCode() porque Point3f lo reemplaza.

Así que, en realidad mi pregunta es doble:

  • ¿Debo incluso quieren un poco profunda equals() tales?
  • En caso afirmativo, ¿cómo obtengo la dirección del objeto para calcular el código hash?

Editar: me acaba de ocurrir algo ... podría generar un valor aleatorio int en la creación de objetos, y el uso que para el código hash. ¿Es eso una buena idea? Por qué no)?

Respuesta

10

O usa System.identityHashCode() o usa IdentityHashMap.

+0

En realidad ... eso es exactamente lo que necesitaba: D – Thomas

-2

La función hashCode() se hereda de Object y funciona exactamente como lo desea (en el nivel del objeto, no en el nivel de coordenadas). No debería haber necesidad de cambiarlo.

En cuanto a su método equals, no hay razón para usarlo, ya que puede hacer obj1 == obj2 en su código en lugar de usar equals, ya que está destinado a la clasificación y similar, donde la comparación de coordenadas hace mucho más sentido.

+0

No tengo control sobre qué comparación utiliza el HashMap ... – Thomas

1

System.identityHashCode() devuelve el mismo código hash para el objeto dado como sería devuelto por el método predeterminado hashCode(), sea o no la clase del objeto dado anula hashCode().

0

Utiliza un delegado aunque este answer es probablemente mejor.


class Vertex extends Point3f{ 
    private final Object equalsDelegate = new Object(); 
    public boolean equals(Object vertex){ 
     if(vertex instanceof Vertex){ 
     return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate); 
     } 
     else{ 
     return super.equals(vertex); 
     } 
    } 
    public int hashCode(){ 
     return this.equalsDelegate.hashCode(); 
    } 
} 
0

Para su información, el método es igual a no violar el contrato de iguales (por contrato del objeto base que sea) ... que es básicamente el método equals para el método objeto de base, así que si quieres identidad es igual vez del Vertex es igual, eso está bien.

En cuanto al código hash, realmente no necesita cambiarlo, aunque la respuesta aceptada es una buena opción y será mucho más eficiente si su tabla hash contiene muchas claves de vértice que tienen los mismos valores .

La razón por la que no es necesario cambiarla es porque está completamente bien que el código hash devuelva el mismo valor para los objetos que devuelve falso ... incluso es un código hash válido para simplemente devolver 0 todos el tiempo para CADA instancia. Si esto es eficiente para las tablas hash es un problema completamente diferente ... usted tendrá muchas más colisiones si muchos de sus objetos tienen el mismo código hash (lo cual puede ser el caso si deja el código hash solo y tiene muchos vértices) con los mismos valores).

Por favor, no acepto esto como la respuesta aunque, por supuesto (lo que usted eligió es mucho más práctico), sólo quería darle un poco más de información de fondo sobre códigos hash y es igual ;-)

+0

Viola el contrato, porque Point3f.equals (Vertex) podría devolver true, mientras que la llamada opuesta siempre devolvería false. Por lo tanto, mi implementación no es simétrica. – Thomas

+0

También me doy cuenta de que el hashCode() predeterminado es válido, pero está haciendo más de lo necesario. Y dado que uso HashTable y HashSet bastante, pensé que sería una ganancia de rendimiento. – Thomas

+0

bueno, viola el método Point3f igual que, el método Object es igual a ... que es a lo que me refería (y en qué tablas hash están interesados) –

0

Por qué ¿desea anular hashCode() en primer lugar? Te gustaría hacerlo si quieres trabajar con alguna otra definición de igualdad. Por ejemplo

public class A { int id;

public boolean equals (A otra) {return other.id Identificación ==} int hashCode pública() {return Identificación;}

} donde desea estar claro que si los identificadores son los mismos entonces los objetos son los mismos, y reemplaza el código hash para que no pueda hacer esto:

HashSet hash = new HashSet(); hash.add (nuevo A (1)); hash.add (nuevo A (1)); y obtenga 2 A's idénticos (desde el punto de vista de su definición de igualdad). El comportamiento correcto sería que solo tendría 1 objeto en el hash, la segunda escritura sobrescribiría.

+0

Lo que dices es correcto, pero no veo tu punto. – Thomas

0

Dado que no está utilizando iguales como una comparación lógica, sino física (es decir, es el mismo objeto), la única forma en que garantizará que el código hash devolverá un valor único es implementar una variación de su propia sugerencia. En lugar de generar un número aleatorio, use UUID para generar un valor único real para cada objeto.

El System.identityHashCode() funcionará, más parte del tiempo, pero no está garantizada como el método Object.hashCode() es no garantizada para devolver un valor único para cada objeto. He visto el caso marginal y probablemente dependerá de la implementación de VM, que no es algo de lo que querrás que dependa tu código.

Extracto de los javadocs para Object.hashCode(): Tanto como sea razonablemente práctico, el método hashCode definido por la clase Object devuelve enteros distintos para distintos objetos. (Esto normalmente se implementa convirtiendo la dirección interna del objeto en un entero, pero esta técnica de implementación no es requerida por el lenguaje de programación JavaTM).

El problema al que se enfrentan es el caso de tener dos objetos puntuales separados sobrescribiéndose unos a otros cuando se insertan en el hashmap porque ambos tienen el mismo hash. Como no hay equivalentes lógicos, con la anulación adicional de hashCode(), el método identityHashCode puede en realidad provocar que ocurra este escenario. Donde el caso lógico solo reemplazaría las entradas hash para el mismo punto lógico, usar el hash basado en el sistema puede hacer que ocurra con dos objetos cualquiera, la igualdad (e incluso la clase) ya no es un factor.

+0

System.identityHashCode() está bien, porque hashCode() no es * expected * para devolver valores distintos para objetos distintos; solo se requiere devolver valores iguales para objetos iguales. – Thomas

Cuestiones relacionadas