Méthode hashCode () lorsque equals () est basé sur plusieurs champs indépendants

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

  •  20-08-2019
  •  | 
  •  

Question

J'ai une classe dont l'égalité est basée sur 2 champs tels que si l'un ou l'autre est égal, les objets de ce type sont considérés comme égaux. comment puis-je écrire une fonction hashCode () pour un tel equals () de sorte que le contrat général de hashCode étant égal lorsque equals renvoie true, elle est préservée?

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;
  }
}

comment puis-je écrire une fonction hashCode () pour cette classe? et je veux éviter le cas trivial ici de retourner une constante comme celle-ci:

public int hashCode() {
  return 1;
}
Était-ce utile?

La solution

Je ne pense pas qu'un hashcode non trivial existe. De même, votre equals() viole le contrat général en tant que indiqué dans l'API : il n'est pas transitif :

(1,2) est égal à (1,3)

(4,3) est égal à (a,b)

Mais (c,d) n'est pas égal à h(a,b) ≠ h(c,d).

Par souci d'exhaustivité, je vous présente le Skeet - Niko preuve =)

Revendication : Le code de hachage doit être la fonction constante triviale.

Preuve : Soit (a,d) et h(a,d) = h(a,b) = h(c,d) deux objets avec des codes de hachage distincts, c'est-à-dire <=>. Considérons l'objet <=>. Selon la définition du PO, <=> est égal à <=> et <=> est égal à <=>. Cela découle du contrat de hashcode que <=>; une contradiction.

Autres conseils

Ok, dans votre scénario, ignorant les exigences de l'API pendant une seconde, il n'y a pas de fonction de hachage non constante

.

Imaginez qu'il y ait une fonction de hachage qui a différentes valeurs pour

(a, b), (a, c), b! = c, puis dièse (a, b)! = dièse (a, c), même si (a, b) = (a, c).

De même, (b, a) et (c, a) doivent émettre le même hashCode.

Appelons notre fonction de hachage h. Nous trouvons:

h (x, y) = h (x, w) = h (v, w) pour tout x, y, v, w.

Par conséquent, la seule fonction de hachage qui fait ce que vous voulez est constante.

Je suis presque sûr que Zach a raison - il n'y a pas de code de hachage non trivial pour le faire.

Pseudo-épreuve:

Considérons deux valeurs différentes, X = (id1, nom1) et Y = (id2, nom2).

Considérons maintenant Z = (id2, nom1). Ceci est égal à la fois à X et à Y, il doit donc avoir le même hashcode que X et Y. Par conséquent, X et Y doivent avoir le même hashcode - ce qui signifie que toutes les valeurs doivent avoir le même hashcode.

Il y a une raison pour laquelle vous vous retrouvez dans une situation étrange: vous cassez la nature transitive des égaux. Le fait que X.equals (Z) et Z.equals (Y) devrait signifie que X.equals (Y) - mais ce n'est pas le cas. Votre définition de l'égalité ne convient pas au contrat normal d'égaux.

Je pense que vous ne pouvez pas. La raison en est que votre equals() méthode n'est pas transitive.

Transitivité signifie pour trois x, y, z non nuls, si x.equals(y), y.equals(z), puis x.equals(z). Dans votre exemple, un objet x={id: 1, name: "ha"}, y={id: 1, name: "foo"}, z={id: 2, name: "bar"} possède cette propriété (x.equals(y) and y.equals(z)). Cependant, false est f(x)==f(y). Chaque méthode x==y doit avoir cette propriété, voir la documentation de l'API Java.

Retour aux fonctions de hachage: Chaque fonction donne une équivalence définie par <=>. Cela signifie que si vous souhaitez comparer les valeurs de la fonction et que vous voulez qu'il soit vrai si <=> (et éventuellement dans d'autres cas), vous obtiendrez une relation transitive, ce qui signifie que vous devez au moins envisager une fermeture transitive de équivalence des objets. Dans votre cas, la fermeture transitive est la relation triviale (tout équivaut à rien). Cela signifie que vous ne pouvez distinguer différents objets par aucune fonction.

Avez-vous intentionnellement défini l'égalité comme lorsque les identifiants sont égaux OU les noms sont égaux .. Ne devrait-il pas être le & "OU &"; être un " ET " ?

Si vous vouliez dire " ET " alors votre hashcode devrait être calculé en utilisant les mêmes champs ou moins (mais n'utilisez jamais de champs non utilisés par equals), vous êtes par equals ().

Si vous vouliez dire " OU " alors votre r hashgcode ne devrait pas inclure id ou name dans son calcul de hashcode, ce qui n’a pas vraiment de sens.

EDIT: Je n’ai pas lu la question attentivement.

-

Je vais utiliser le bocal commons-lang.

XOR, les membres hashCode devraient fonctionner. Comme il se doit, implémente correctement hashCode () et equals ().

Cependant, votre code peut être erroné si vous ne protégez pas votre hashCode. Une fois qu'il a été haché, il ne devrait pas être changé. Il faut empêcher que cela se produise.

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

ou

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

ou

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();
   }
}

Après avoir relu la question.

Vous pouvez renseigner automatiquement un autre champ lors de la mise à jour de l'un d'entre eux.

-

EDIT: mon code peut dire mieux que mon anglais.

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

EDIT 2:

Le code de la question peut être erroné, car la valeur retournée sera toujours vraie à moins que vous n'ayez défini l'id & amp; nom.

Si vous voulez vraiment une méthode faisant des égaux partiels, créez votre propre API nommée "ielle partial () "..

.

Le chemin le plus simple consiste à XOR les codes de hachage de chaque champ individuel. Cela a une laideur mineure dans certaines situations (par exemple, en coordonnées X, Y, cela entraîne la situation potentiellement mauvaise de hachages égaux lorsque vous retournez X et Y), mais est globalement assez efficace. Modifiez au besoin pour réduire le nombre de collisions, si nécessaire, afin de gagner en efficacité.

Que diriez-vous de cela

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

La fonction doit toujours renvoyer un " valide " hash ...

Edit: vous venez de remarquer que vous utilisez " ou " pas " et " : P ben je doute qu'il y ait une bonne solution à ce problème ...

Que diriez-vous de

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top