Método hashCode() cuando es igual() se basa en múltiples campos independientes

StackOverflow https://stackoverflow.com/questions/479105

  •  20-08-2019
  •  | 
  •  

Pregunta

Tengo una clase cuya igualdad se basa en 2 campos de modo que si alguno de ellos es igual entonces los objetos de este tipo se consideran iguales.¿Cómo puedo escribir una función hashCode() para igual a() de modo que se conserve el contrato general de que hashCode sea igual cuando igual devuelve verdadero?

public class MyClass {
  int id;
  String name;

  public boolean equals(Object o) {
    if (!(o instanceof MyClass))
      return false;
    MyClass other = (MyClass) o;
    if (other.id == this.id || other.name == this.name)
      return true;
    return false;
  }
}

¿Cómo escribo una función hashCode() para esta clase?y quiero evitar el caso trivial de devolver una constante como esta:

public int hashCode() {
  return 1;
}
¿Fue útil?

Solución

No creo que exista un código hash no trivial.También tu equals() viola el contrato general como indicado en la API --- es no transitivo:

(1,2) es igual (1,3)

(4,3) es igual (1,3)

Pero (4,3) es no es igual a (1,2).


En aras de la exhaustividad, les presento el Tiro al plato-niko prueba =)

Afirmar: El código hash debe ser la función constante trivial.

Prueba:Dejar (a,b) y (c,d) ser dos objetos con códigos hash distintos, es decir h(a,b) ≠ h(c,d).Considere el objeto (a,d).Según la definición del OP, (a,d) es igual a (a,b), y (a,d) es igual a (c,d).Se desprende de la contrato de código hash eso h(a,d) = h(a,b) = h(c,d);una contradicción.

Otros consejos

Ok, en su escenario, ignorando los requisitos de la API por un segundo, no hay una función hash no constante

Imagine que hubiera una función hash que tiene valores diferentes para

(a, b), (a, c), b! = c, luego hash (a, b)! = hash (a, c), aunque (a, b) = (a, c).

Del mismo modo, (b, a) y (c, a) deben emitir el mismo código hash.

Llamemos a nuestra función hash h. Encontramos:

h (x, y) = h (x, w) = h (v, w) para todo x, y, v, w.

Por lo tanto, la única función hash que hace lo que quieres es constante.

Estoy bastante seguro de que Zach tiene razón: no hay un código hash no trivial para hacer esto.

Pseudo-prueba:

Considere dos valores no iguales, X = (id1, name1) e Y = (id2, name2).

Ahora considere Z = (id2, name1). Esto es igual a X e Y, por lo que debe tener el mismo código hash que X e Y. Por lo tanto, X e Y deben tener el mismo código hash, lo que significa que los valores todos deben tener el mismo código hash.

Hay una razón por la que te has metido en una situación extraña: estás rompiendo la naturaleza transitiva de los iguales. El hecho de que X.equals (Z) y Z.equals (Y) debería significa que X.equals (Y), pero no es así. Su definición de igualdad no es adecuada para el contrato normal de iguales.

Creo que no puedes. La razón es que su método equals() no es transitivo.

Transitividad significa para tres no nulos x, y, z, si x.equals(y), y.equals(z), entonces x.equals(z). En su ejemplo, un objeto x={id: 1, name: "ha"}, y={id: 1, name: "foo"}, z={id: 2, name: "bar"} tiene esta propiedad (x.equals(y) and y.equals(z)). Sin embargo, false es f(x)==f(y). Todos los métodos x==y deben tener esta propiedad, consulte los documentos de la API de Java.

Volver a las funciones de hashing: cada función produce una equivalencia definida por <=>. Eso significa que si está interesado en la comparación de los valores de la función y desea que devuelva verdadero si <=> (y posiblemente en otros casos), recibirá una relación transitiva, lo que significa que debe considerar al menos un cierre transitivo de equivalencia de objetos. En su caso, el cierre transitivo es la relación trivial (todo es igual a cualquier cosa). Lo que significa que no puede distinguir diferentes objetos por ninguna función.

¿Ha definido intencionalmente la igualdad como cuando los identificadores son iguales O los nombres son iguales? ¿No debería " OR " ser un " Y " ?

Si quisiste decir " AND " entonces su código hash debe calcularse usando los mismos campos o menos (pero nunca use campos que no sean utilizados por iguales) que es por iguales ().

Si quisiste decir " O " entonces su hashgcode no debe incluir id o nombre en su cálculo de hashcode, lo que realmente no tiene sentido.

EDITAR: no leí la pregunta con cuidado.

-

Usaré commons-lang jar.

XOR los miembros hashCode deberían funcionar. Como deberían implementar hashCode () y equals () correctamente.

Sin embargo, su código puede ser incorrecto si no protege su código hash. Una vez que se ha procesado, no se debe cambiar. Debe evitarse que suceda.

public hashCode(){
   return new AssertionError();
}

o

 public class MyClass {
   final int id;
   final String name;
   // constructor
 }

o

public class MyClass {
   private int id;
   private String name;
   boolean hashed=false;
   public void setId(int value){
     if(hashed)throw new IllegalStateException();
     this.id=value;
   }
   public void setName(String value){
     if(hashed)throw new IllegalStateException();
     this.name=value;
   }
   // your equals() here
   public hashCode(){
     hashed=true;
     return new HashCodeBuilder().append(id).append(name).toHashCode();
   }
}

Después de volver a leer la pregunta.

Puede completar automáticamente el otro campo cuando se actualiza uno de ellos.

-

EDITAR: mi código puede decir mejor que mi inglés.

void setName(String value){
  this.id=Lookup.IDbyName(value);
}
void setID(String value){
  this.name=Lookup.NamebyId(value);
}

EDITAR 2:

El código en la pregunta puede ser incorrecto ya que siempre devolverá verdadero a menos que haya configurado tanto el id & amp; nombre.

Si realmente desea un método que haga iguales parciales, cree su propia API que se llame " partialEquals () " ;.

La ruta más simple es XOR los códigos hash de cada campo individual. Esto tiene una fealdad menor en algunas situaciones (por ejemplo, en las coordenadas X, Y, causa la situación potencialmente pobre de tener hashes iguales cuando voltea X e Y), pero en general es bastante efectivo. Ajuste según sea necesario para reducir las colisiones si es necesario para la eficiencia.

¿Qué tal esto?

public override int GetHashCode()
{
    return (id.ToString() + name.ToString()).GetHashCode();
}

La función siempre debe devolver un " válido " hash ...

Editar: acabo de notar que usas " o " no " y " : P bueno, dudo que haya una buena solución a este problema ...

¿Qué tal

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top