2012-03-10 23 views
16

Tengo problemas para escribir un método hashCode() para una clase que creé. Esta clase está destinada a ser utilizada dentro de un TreeSet, y como tal, implementa Comparable. La clase tiene las siguientes variables:Creando un método hashCode() - Java

public class Node implements Comparable<Node> { 
    Matrix matrix; 
    int[] coordinates= new int[2]; 
    Node father; 
    int depth; 
    int cost; 

Aquí está la implementación del método compareTo(). Quiero que TreeSet organice estas estructuras de nodo por su costo, por lo tanto, compareTo() devuelve el resultado de una simple resta.

public int compareTo(Node nodeToCompare) { 
    return this.cost - nodeToCompare.cost; 
} 

También he implementado un método equals().

public boolean equals(Object objectToCompare) { 
    if(objectToCompare== this) {return true;} 
    if(objectToCompare== null || objectToCompare.getClass()!= this.getClass()) {return false;} 

    Node objectNode= (Node) objectToCompare; 
    return this.father.equals(objectNode.father) && 
      this.depth== objectNode.depth && 
      this.cost== objectNode.cost && 
      this.matrix.equals(objectNode.matrix) && 
      Arrays.equals(this.coordinates, objectNode.coordinates); 
} 

Una vez dicho todo esto, tengo un par de preguntas:

  1. Desde que puso en práctica un nuevo método equals(), debo poner en práctica un nuevo método hashCode()?
  2. ¿Cómo puedo implementar un nuevo hashCode method() con esas variables? (Tenga en cuenta que la matriz de variables del tipo Matrix tiene implementado un método hashCode())

¡Eso es todo!

Respuesta

19

Su método compareTo no es consistente con el método de equals: su método de compareTo dice que dos casos son equivalentes si tienen el mismo cost — tal que una TreeSet tan sólo puede contener como máximo una instancia con un determinado cost — pero su equals método dice que solo son equivalentes si tienen el mismo costy son iguales en varias otras formas.

Así, en el supuesto de que su método de equals es correcta:

  • que necesita para reparar su método compareTo ser coherente con ella.
  • necesita crear un método hashCode que sea coherente con él. Recomiendo usar el mismo tipo de lógica que usa el java.util.List.hashCode(), que es una forma directa y efectiva de ensamblar los códigos hash de los objetos componentes en un orden específico; Básicamente podría escribir algo como:
    int hashCode = 1; 
    hashCode = 31 * hashCode + (father == null ? 0 : father.hashCode()); 
    hashCode = 31 * hashCode + depth; 
    hashCode = 31 * hashCode + cost; 
    hashCode = 31 * hashCode + matrix.hashCode(); 
    hashCode = 31 * hashCode + java.util.Arrays.hashCode(coordinates); 
    return hashCode;
+0

Pero si cambio el método compareTo, ¿eso no cambiará la forma en que se organizan las cosas en un TreeSet? –

+1

@ GonçaloLourenço: Sí. Ese es mi punto; en este momento, si 'n1.cost == n2.cost', entonces' TreeSet' que contiene 'n1' no puede contener' n2'. ¿Es eso realmente lo que quieres? – ruakh

+0

Definitivamente no. Supongo que tendré que usar algo más en lugar de TreeSet. Como usted fue el primero en responder mi pregunta, marcaré su respuesta como la correcta. ¡Gracias por toda la ayuda! –

7

Intellij IDEA puede hacer esto como una función de 'clic derecho'. Solo verlo hecho correctamente te enseñará mucho.

Y debe anular ambos en cualquier caso.

+2

similares en Eclipse: Fuente> Generar hashCode() y equals() ... –

+0

@ToKra en mi caso , agregó esta línea 'result = prime * result + getOuterType(). hashCode();', pero quería que solo los campos tuvieran código hash consistente (entre recargas de la aplicación), por lo tanto, al comentar esa línea funcionó correctamente. Aunque era una clase interna, esa es la razón por la que esa línea apareció. –

3

El contrato para el método hashCode establece que si dos objetos son iguales, entonces llamando hashCode() le proporcionará el mismo resultado entero. Lo contrario no tiene que ser cierto, es decir, si dos hashCodes son lo mismo, los objetos no tienen que ser iguales entre sí.

En cuanto a su método equals (que necesita una traducción variable por cierto), puede agregar los hashCodes de todas las variables miembro internas que deben ser iguales para que su método equiv sea verdadero. p.ej.

public int hashCode() { 
    return this.matrix.hashCode() + 
      this.coordinates[0] + 
      this.coordinates[1] + 
      this.father.hashCode() + 
      this.depth + this.cost;    
} 

Lo anterior supone que la matriz y el padre nunca son valores nulos, lo que necesita para asegurarse de que compruebe si hay valores nulos si ese no es el caso.

Si te sientes más aventurero, puedes multiplicar algunas de las anteriores para asegurarte de que no obtienes colisiones hashCode para diferentes datos (esto ayudará a mejorar el rendimiento si utilizas tu clase en hashTables y hashMaps). Si usted necesita para atender a los nulos, el método anterior se puede escribir un poco mejor así:

public int hashCode() { 
    return ((this.matrix == null) ? 0 : this.matrix.hashCode()) + 
      17 * this.coordinates[0] + 
      this.coordinates[1] + 
      ((this.father == null) ? 0 : this.father.hashCode()) + 
      31 * this.depth + 19 * this.cost;    
} 
+0

Tenga en cuenta que 'father' es del tipo' Node', por lo que si nunca es 'null', entonces su enfoque dará como resultado una recursión infinita y un desbordamiento de la pila. – ruakh

+0

Sí, tenía miedo de eso. Supongo que tendré que cambiar algunas cosas. ¡Gracias por responder mi pregunta! –

+0

Es cierto, por lo tanto, tenemos que suponer que a) el padre es nulo o ob) no hay rutas circulares en el gráfico. Si ninguno de los anteriores es verdadero, entonces deberíamos excluir al padre del cálculo de hashCode. – ksymeon

0

Si su colección es pequeña puede devolver constante del método hashCode. Se usa para encontrar rápidamente. hashCodes es como las cajas, que mantienen elementos. Las reglas son:

  1. Los elementos iguales deben estar en la misma caja (tienen el mismo hashCode) - seguramente;
  2. No elementos iguales pueden estar en la misma casilla o en cajas diferentes.

Luego regresas constante, obedeces estas 2 reglas, pero puede disminuir significativamente el rendimiento en listas no pequeñas (porque JVM buscará en todos los elementos, y no en elementos en el mismo cuadro solamente). Pero el retorno constante es el mal enfoque.

PD: Perdón por mi escritura. El inglés no es mi lengua materna

PPS: usualy que tienen que poner en práctica el método hashCode de la misma manera como iguales (use mismos elementos)

Cuestiones relacionadas