64 votes

Utilisez Objects.hash () ou implémentez hashCode ()?

J'ai récemment découvert l' Objects.hash() méthode.

Ma première pensée a été que cela remet de l'ordre dans votre hashCode() de la mise en œuvre d'un lot. Voir l'exemple suivant:

@Override
//traditional
public int hashCode() {
    int hash = 5;
    hash = 67 * hash + (int)(this.id ^ (this.id >>> 32));
    hash = 67 * hash + (int)(this.timestamp ^ (this.timestamp >>> 32));
    hash = 67 * hash + Objects.hashCode(this.severity);
    hash = 67 * hash + Objects.hashCode(this.thread);
    hash = 67 * hash + Objects.hashCode(this.classPath);
    hash = 67 * hash + Objects.hashCode(this.message);
    return hash;
}

@Override
//lazy
public int hashCode() {
    return Objects.hash(id, timestamp, severity, thread, classPath, message);
}

Même si je dois dire que cela semble trop beau pour être vrai. Aussi, je n'ai jamais vu ce type d'utilisation.

Existe-il des inconvénients de l'utilisation de Objects.hash() par rapport à la mise en œuvre de votre propre code de hachage? Quand devrais-je utiliser chacune de ces approches?

Mise à jour

Bien que ce sujet est marqué comme résolu, n'hésitez pas à continuer à poster des réponses qui fournissent de nouvelles informations et de leurs préoccupations.

71voto

Andy Turner Points 13883

Notez que le paramètre de Objects.hash est Object... . Cela a deux conséquences principales:

  • Les valeurs primitives utilisées dans le calcul du code de hachage doivent être encadrées. Par exemple, this.id est converti de long à Long .
  • Un Object[] doit être créé pour appeler la méthode.

Le coût de la création de ces objets "inutiles" peut augmenter si hashCode est appelé fréquemment.

30voto

nagendra547 Points 1847

Vous trouverez ci - dessous l' implémentation d' Objects.hash - qui appelle Arrays.hashCode en interne.

 public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
 

Ceci est l'implémentation de la méthode Arrays.hashCode

 public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}
 

Je suis donc d’accord avec @Andy. Le coût de la création de ces objets "inutiles" peut s’ajouter si hashCode est appelé fréquemment. Si vous implémentez vous-même, ce serait plus rapide.

14voto

Tezra Points 4908

Je voudrais essayer de faire un argument fort pour les deux.

L'ouverture d'avertissement

Pour répondre à cette question, Objects.hash(), Objects.hashCode(), et de toute fonction fournie par une bibliothèque qui remplit ce rôle, sont interchangeables. Tout d'abord, j'aimerais dire, utiliser Objects.hash() ou de ne pas utiliser les Objets statiques de toutes fonctions. Aucun argument pour ou contre cette méthode nécessite de faire des hypothèses sur le code compilé qui ne sont pas garantis pour être vrai. (Par exemple, le compilateur optimiseur peut convertir l'appel de la fonction dans une ligne d'appel, contournant ainsi le supplément de la pile des appels et de l'allocation des objets. Tout comme l'utilisation des boucles qui ne sont rien d'utile ne pas se rendre à la version compilée (sauf si vous désactivez l'optimiseur). Vous aussi vous n'avez aucune garantie que les futures versions de Java ne pas inclure la version JVM comme C# dans sa version de cette méthode. (pour des raisons de sécurité je crois)) de Sorte que le seul argument que vous pouvez faire concernant l'utilisation de cette fonction, c'est qu'il est généralement plus sûr de laisser les détails d'un bon hachage à cette fonction que d'essayer de mettre en place votre propre version naïve.

Pour Les Objets.de hachage

  • Garanti pour être un bon hachage.
  • Prend 5 secondes pour le mettre en œuvre.
  • La vôtre aurait eu un bug (en quelque sorte, surtout si vous copie-collé de la mise en œuvre)

Contre Des Objets.de hachage

  • La Java docs fais pas de promesses à propos de hachage de la croix-compatibilité (une JVM v6 et de la JVM v8 donnent les mêmes valeurs? toujours? à travers les OS?)
  • La chose à propos de hashCodes, Ils fonctionnent mieux si "uniformément répartie". Donc, si un int valeur est valide uniquement pour la plage de 1 à 100, vous pouvez "redistribuer" son hash-codes pour ne pas faire partie de la même seau.
  • Si vous avez une exigence qui fait de vous remettre en question la manière dont les Objets.hachage des œuvres, de la fiabilité/performance sage, Pensez-y si le hash-code est vraiment ce que vous voulez, et de mettre en œuvre une coutume de hachage méthode de codage qui répond à vos besoins.

9voto

Ihor M. Points 580

Joshua Bloch dans son livre Effective Java, 3ème édition, p. 53 décourage l'utilisation de Objects.hash(...) si les performances sont critiques.

Les primitives sont en cours de sélection automatique et la création d'un tableau Object est pénalisée.

1voto

Luke Usherwood Points 687

Personnellement, je préfère le côté avec le code court en premier, car il est beaucoup plus rapide à lire, modifier et vérifier que correct, qui tous servent à éviter des bugs lors de la modification de la classe.

Alors pour les critiques des performances des classes ou où les champs sont coûteux pour hacher, je voudrais mettre en cache le résultat (comme String t):

// volatile not required for 32-bit sized primitives
private int hash;

@Override
public final int hashCode() {
    // "Racy Single-check idiom" (Item 71, Effective Java 2nd ed.)
    int h = hash;
    if (h == 0) {
        h = Objects.hash(id, timestamp, severity, thread, classPath, message);
        hash = h;
    }
    return h;
}

Dans ce lockless-thread-safe modèle (qui suppose une immuable classe, naturellement) il y a une petite chance qu' hash pourrait obtenir initialisée par différents threads plusieurs fois, mais cela n'a pas d'importance, parce que le résultat de la méthode publique est toujours identique. La clé (mémoire de visibilité) exactitude est de s'assurer que hash n'est jamais écrit et lu plus d'une fois dans la méthode.

Le tableau-construction de la peine de Objects.hash pourrait être plus bas que vous ne l'imaginez une fois que le code est JIT-inline & optimisée par l'C2 compilateur. Heureusement, le bon JDK gens ont quelque chose de la cuisson qui doit essentiellement d'éliminer tous les frais généraux de l'utiliser: JEP 348: Compilateur Java Intrinsèques pour l'Api du JDK

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X