7
votes

Java hashcode d'un champ

Edit: Préparez mes objets pour l'utilisation dans un hachema.

Après avoir lu un peu la façon de générer un code de hachage, je suis un type de confusion maintenant. Ma question (probablement triviale) est, comment dois-je mettre en œuvre une méthode de code HASHCODE lorsque j'ai un seul domaine que je pourrais utiliser? Puis-je utiliser les fiels directement? Si je comprends bien correctement, les valeurs de HashCode ne doivent pas changer pendant la durée de vie d'un objet, et je n'ai qu'un identifiant déposé qui correspond à cela, mais j'ai lu ailleurs, que l'on ne devrait pas utiliser ID ... Des despides de cela, comment Une fonction hashcode serait-elle basée sur celle-ci (une valeur unique et non changeante)? La méthode Equals est également basée sur l'identifiant uniquement ..


2 commentaires

@Poly si j'ai bien compris correction, comme IIRC, si je me souviens bien.


L'ambiguïté de IIC a décidé de "si je me souviens bien".


3 Réponses :


17
votes

Si votre objet est mutable, il est acceptable de modifier son code de hachage au fil du temps. Bien sûr, vous devriez préférer des objets immuables ( Efficace Java 2nd édition, point 15: minimiser l'utabilité em>).

Voici la recette de hashcode de Josh Bloch, de Efficient Java 2nd Edition, point 9 : Toujours remplacer hashcode code> lorsque vous remplacez égale code> em>: p>

Recette de code de hachage Java 2nd édition efficace h3>
  • Stockez une valeur constante non nulle, disons 17, dans une variable int code> appelé résultat code>. li>.
  • Compute un int code> hashcode C code> pour chaque champ:
    • Si le champ est un booléen code>, calculez (f? 1: 0) code> li>
    • Si le champ est un octet, char, court, int code>, calculez (int) f code> li>
    • Si le champ est un long code>, calculez (int) (f ^ (f >>> 32)) code> li>
    • Si le champ est un float code>, calculez float.floattingbits (f) code> li>
    • Si le champ est un double code>, calculez double.doubletolongbits (f) code>, puis hachage le long code> comme dans ci-dessus. li>
    • Si le champ est une référence d'objet et la méthode de cette classe est égale code> compare le champ en appelant de manière récursive égale code>, invoquer de manière récursive hashcode code> sur le champ . Si la valeur du champ est null code>, retour 0. li>
    • Si le champ est un tableau, traitez-le comme si chaque élément est un champ distinct. Si chaque élément d'un champ de tableau est significatif, vous pouvez utiliser l'un des méthodes TRAASAYS.HASTCODE CODE> Ajouté dans la version 1.5. LI> ul> li>
    • combine le hashcode C code> dans résultat code> comme suit: résultat = 31 * résultat + c; code> li> li> ul>

      Il serait correct de suivre la recette tel quel, même avec un seul champ. Faites juste l'action appropriée en fonction du type de champ. P>

      Notez qu'il existe des bibliothèques qui simplifient cela pour vous, par exemple. hashcodebuilder code> de Apache Commons Lang ou juste arrairies.hashscode / deevhashCode code> de java.util.arrays code> . p>

      Ces bibliothèques vous permettent de simplement écrire quelque chose comme ceci: p>

      import org.apache.commons.lang.builder.*;
      
      public class CustomType implements Comparable<CustomType> {
          // constructors, etc
          // let's say that the "significant" fields are field1, field2, field3
          @Override public String toString() {
              return new ToStringBuilder(this)
                  .append("field1", field1)
                  .append("field2", field2)
                  .append("field3", field3)
                      .toString();
          }
          @Override public boolean equals(Object o) {
              if (o == this) { return true; }
              if (!(o instanceof CustomType)) { return false; }
              CustomType other = (CustomType) o;
              return new EqualsBuilder()
                  .append(this.field1, other.field1)
                  .append(this.field2, other.field2)
                  .append(this.field3, other.field3)
                      .isEquals();
          }
          @Override public int hashCode() {
              return new HashCodeBuilder(17, 37)
                  .append(field1)
                  .append(field2)
                  .append(field3)
                      .toHashCode();
          }
          @Override public int compareTo(CustomType other) {
              return new CompareToBuilder()
                  .append(this.field1, other.field1)
                  .append(this.field2, other.field2)
                  .append(this.field3, other.field3)
                      .toComparison();
          }
      }
      


4 commentaires

+1 pour cette réponse informative sur HashCode et la suggestion de tableau.Hashcode (); Première fois que j'ai vu ça et j'aime ça!


Merci encore pour cette longue et bonne réponse compréhensible. Mais j'ai manqué d'autres points - pour l'instant, je n'ai pas à mettre en œuvre un hashcode (voir Michaels Commentaire), mais peut-être que je dois à l'avenir, votre réponse est donc trop tôt à temps. ;)


Depuis Java SE 7, vous pouvez utiliser Objets # Hash (objet ...) .


Veuillez ajouter la raison pour laquelle nous devons multiplier chaque hachage de valeur calculée précédente, c'est une information agréable.



2
votes

Si vous voulez que des objets avec différents identifiants identifiés par cet identifiant, tout ce que vous avez à faire est de le renvoyer / le comparer.

private final int id;

public int hashCode() { return id; }

public boolean equals(Object o) { 
    return o instanceof ThisClass && id == ((ThisClass)o).id;
}


4 commentaires

Votre fonction Equals n'est pas implémentée de la manière correcte, mais simplement de retourner l'identifiant comme hashvalue, je compte comme réponse à ma question, donc +1.


Quel est le problème avec les égaux ()? Si seul le champ d'identification est suffisant pour remplacer HashCode (), Peters Equals () La mise en œuvre semble parfaitement bien.


@KRMBY HM, AFAIK Il doit y avoir un chèque nul afin qu'il retourne faux dans ce cas.


L'instanceof fera le Nullcheck pour vous. Cela est égal () est moche, mais semble être correct.



1
votes

Peu importe que le nombre de champs sont utilisés pour calculer HashCode. Mais il compte travailler avec égaux () . Si un est égal à B, leur hachage doit être identique.

Si vous détestez HashCode :) et votre objet ne sera jamais mis aux conteneurs à base de hash (HASHMAP, HASHSET ..), il suffit de quitter HashCode () seul, laissez la classe de base à calculer HashCode.


4 commentaires

Bon point, vous n'avez pas besoin d'implémenter des égaux / HashCode pour la plupart des objets.


Je demande parce que j'utilise maintenant un hashmap pour obtenir le O (1) fois pour obtenir une entrée.


@Insernickhere: Désolé d'avoir mal lu votre question. Assurez-vous que votre objet est mis sur HASHMAP comme clé pas une valeur.


Oh, tu vois, tu m'as signalé quelque chose que je me trompe totalement. Je pensais que je dois écrire du code de hasch Evan si j'utilise ma classe comme des valeurs. D'Oh, à quel point puis-je être stupide? ;-)