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

J’ai une classe dont l’égalité est basée sur 2 champs tels que si l’un ou l’autre est égal, les objects de ce type sont considérés é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 vrai, est préservé?

public class MyClass { int id; Ssortingng 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 sortingvial de retourner une constante comme ceci:

 public int hashCode() { return 1; } 

Je ne pense pas qu’un hashcode non sortingvial existe. De plus, votre equals() viole le contrat général comme indiqué dans l’API — il n’est pas transitif :

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

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

Mais (4,3) n’est pas égal à (1,2) .


Par souci d’exhaustivité, je vous présente la preuve Skeet – Niko =)

Revendication : Le hashcode doit être la fonction constante sortingviale.

Preuve : Soit (a,b) et (c,d) deux objects avec des codes de hachage distincts, c’est-à-dire h(a,b) ≠ h(c,d) . Considérons l’object (a,d) . Selon la définition de l’OP, (a,d) est égal à (a,b) et (a,d) est égal à (c,d) . Il découle du contrat de hashcode que h(a,d) = h(a,b) = h(c,d) ; une contradiction.

Ok, dans votre scénario, ignorant les exigences de l’API pour 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 hash (a, b)! = hash (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 sûr que Zach a raison – il n’y a pas de hashcode non sortingvial pour faire cela.

Pseudo-preuve:

Considérons deux valeurs non égales, 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 signifier 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 tu ne peux pas. La raison en est que votre méthode equals() n’est pas transitive.

La transitivité signifie pour trois x, y, z non nuls, si x.equals(y) , y.equals(z) , puis x.equals(z) . Dans votre exemple, un object 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, x.equals(z) est false . Chaque méthode equals() 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 f(x)==f(y) . Cela signifie que si vous êtes intéressé par la comparaison des valeurs de la fonction et que vous voulez qu’il retourne la valeur true si x==y (et éventuellement dans d’autres cas), vous obtiendrez une relation transitive, ce qui signifie que vous devez au moins envisager une fermeture transitive. d’équivalence d’objects. Dans votre cas, la fermeture transitive est la relation sortingviale (tout équivaut à rien). Ce qui signifie que vous ne pouvez pas distinguer différents objects par aucune fonction.

Avez-vous intentionnellement défini l’égalité comme lorsque les identifiants sont égaux OU les noms sont égaux .. Le “OU” ne doit-il pas être un “ET”?

Si vous vouliez dire “ET”, votre hashcode devrait être calculé en utilisant des champs identiques ou inférieurs (mais n’utilisez jamais de champs non utilisés par égaux) que vous êtes par égaux ().

Si vous vouliez dire “OU”, alors votre 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 commons-lang jar.

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 Ssortingng name; // constructor } 

ou

 public class MyClass { private int id; private Ssortingng name; boolean hashed=false; public void setId(int value){ if(hashed)throw new IllegalStateException(); this.id=value; } public void setName(Ssortingng 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(Ssortingng value){ this.id=Lookup.IDbyName(value); } void setID(Ssortingng value){ this.name=Lookup.NamebyId(value); } 

EDIT 2:

Le code de la question peut être incorrect, car il retournera toujours vrai à moins que vous ayez défini à la fois l’id et le nom.

Si vous voulez vraiment une méthode faisant des égaux partiels, créez votre propre API nommée “partialEquals ()”.

Et si nous utilisions TreeMap pour remplacer HashMap. Est-il possible d’écrire une méthode compareTo () valide pour MyClass?

super moment duh. essayait d’écrire hashCode () avant de valider le contrat d’equals ().

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 d’avoir des hachages égaux lorsque vous retournez X et Y), mais est globalement assez efficace. Tweak au besoin pour réduire les collisions si nécessaire pour plus d’efficacité.

Que dis-tu de ça

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

La fonction devrait toujours renvoyer un hachage “valide” …

Edit: juste remarqué que vous utilisez “ou” pas “et”: P eh bien je doute qu’il y ait une bonne solution à ce problème …

Que diriez-vous

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