Question

En Java, j'ai une sous-classe Vertex de la classe Java3D Point3f . Maintenant, Point3f calcule equals () en fonction des valeurs de ses coordonnées, mais pour ma classe Vertex , je veux être plus strict: deux sommets ne sont que égaux s'ils sont le même objet. Jusqu'ici, tout va bien:

class Vertex extends Point3f {

    // ...

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

Je sais que cela viole le contrat de equals () , mais comme je ne comparerai que les sommets à d'autres sommets, cela ne pose pas de problème.

Maintenant, pour pouvoir placer des sommets dans un HashMap , la méthode hashCode () doit renvoyer des résultats cohérents avec equals () . Il le fait actuellement, mais sa valeur de retour est probablement basée sur les champs du Point3f , et donnera donc des collisions de hachage pour différents objets Vertex avec les mêmes coordonnées.

Par conséquent, je voudrais baser hashCode () sur l'adresse de l'objet, au lieu de le calculer à partir des champs de Vertex . Je sais que la classe Object remplit cette fonction, mais je ne peux pas appeler sa méthode hashCode () car Point3f la remplace.

Donc, en fait, ma question est double:

  • Devrais-je même vouloir un égal à () ?
  • Si oui, comment puis-je obtenir l'adresse de l'objet pour calculer le code de hachage?

Éditer: je viens de penser à quelque chose ... Je pourrais générer une valeur aléatoire int lors de la création d'objet et l'utiliser pour le code de hachage. est-ce une bonne idée? Pourquoi (pas)?

Était-ce utile?

La solution

Utilisez System.identityHashCode () ou utilisez un IdentityHashMap.

Autres conseils

System.identityHashCode () renvoie le même code de hachage pour l'objet donné que celui qui serait renvoyé par la méthode par défaut hashCode () , que la la classe de l'objet donné remplace hashCode () .

Vous utilisez un délégué même si cette answer est probablement meilleur.


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

Juste pour votre information, votre méthode equals ne viole PAS le contrat equals (pour le contrat de l'objet de base qui est) ... c'est en gros la méthode equals pour la méthode object de base, donc si vous voulez une identité égale à la place du sommet est égal à , c’est bien.

En ce qui concerne le code de hachage, vous n'avez vraiment pas besoin de le changer, bien que la réponse acceptée soit une bonne option et sera beaucoup plus efficace si votre table de hachage contient de nombreuses clés de sommet qui ont les mêmes valeurs.

La raison pour laquelle vous n’avez pas besoin de le changer, c’est parce que c’est très bien que le code de hachage retourne la même valeur pour les objets qui est égal à false. temps pour CHAQUE instance. La question de savoir si cela est efficace pour les tables de hachage est un problème complètement différent ... vous obtiendrez beaucoup plus de collisions si beaucoup de vos objets ont le même code de hachage (ce qui peut être le cas si vous laissez le code de hachage seul et que vous avez beaucoup de sommets avec les mêmes valeurs).

N'acceptez pas cela comme réponse, bien sûr (ce que vous avez choisi est beaucoup plus pratique), je voulais juste vous donner un peu plus d'informations de base sur les codes de hachage et les équivalents; -)

Pourquoi voulez-vous remplacer hashCode () en premier lieu? Vous voudriez le faire si vous voulez travailler avec une autre définition de l'égalité. Par exemple

public class A {   int id;

public boolean est égal à (Un autre) {return other.id == id}   public int hashCode () {return id;}

} où vous voulez être clair que si les identifiants sont les mêmes, les objets sont les mêmes et vous substituez le hashcode pour que vous ne puissiez pas le faire:

HashSet hash = new HashSet (); hash.add (nouveau A (1)); hash.add (nouveau A (1)); et obtenez 2 identiques (du point de vue de votre définition de l'égalité) A. Le comportement correct serait alors que vous n’ayez qu’un seul objet dans le hachage, la deuxième écriture écrasera.

Puisque vous n'utilisez pas equals comme comparaison logique, mais physique (c'est-à-dire qu'il s'agit du même objet), la seule façon de garantir que le code de hachage renverra une valeur unique consiste à implémenter votre propre variante. suggestion. Au lieu de générer un nombre aléatoire, utilisez UUID pour générer une valeur unique réelle pour chaque objet.

System.identityHashCode () fonctionnera plus souvent , mais n'est pas garanti, car la méthode Object.hashCode () n'est pas garantie de retourner un message unique. valeur pour chaque objet. J'ai vu le cas marginal se produire, et cela dépendra probablement de l'implémentation de la VM, ce dont vous ne voudrez pas que votre code dépende.

Extrait des javadocs pour Object.hashCode (): Dans la mesure du possible, la méthode hashCode définie par la classe Object renvoie des entiers distincts pour des objets distincts. (Ceci est généralement implémenté en convertissant l'adresse interne de l'objet en un entier, mais cette technique d'implémentation n'est pas requise par le langage de programmation JavaTM.)

Le problème auquel cette adresse est confrontée est le cas où deux objets ponctuels distincts se superposent lorsqu'ils sont insérés dans la table de hachage, car ils ont tous deux le même hachage. En l'absence d'égaux logiques, avec la substitution associée de hashCode (), la méthode identityHashCode peut en réalité provoquer ce scénario. Lorsque le cas logique ne remplace que les entrées de hachage pour le même point logique, l'utilisation du hachage basé sur le système peut provoquer son apparition avec deux objets quelconques. L'égalité (et même la classe) n'est plus un facteur.

La fonction hashCode () est héritée de Object et fonctionne exactement comme vous le souhaitez (au niveau de l'objet, pas au niveau des coordonnées). Il ne devrait pas être nécessaire de le changer.

En ce qui concerne votre méthode equals, il n'y a aucune raison de l'utiliser, car vous pouvez simplement faire obj1 == obj2 dans votre code au lieu d'utiliser equals, car il est destiné au tri et à des opérations similaires, où la comparaison de coordonnées produit beaucoup plus de sens.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top