web-dev-qa-db-fra.com

Cohérence de hashCode () sur une chaîne Java)

La valeur hashCode d'un Java) La chaîne est calculée comme suit: ( String.hashCode () ):

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

Existe-t-il des circonstances (par exemple, version de JVM, fournisseur, etc.) dans lesquelles l'expression suivante sera évaluée comme étant fausse?

boolean expression = "This is a Java string".hashCode() == 586653468

Mise à jour n ° 1: Si vous affirmez que la réponse est "oui, il y a de telles circonstances", donnez un exemple concret du cas où "ceci est un Java chaîne". ".HashCode ()! = 586653468. Essayez de être aussi spécifique/concret que possible.

Mise à jour # 2: Nous savons tous que compter sur les détails d'implémentation de hashCode () est mauvais en général. Cependant, je parle spécifiquement de String.hashCode () - veuillez donc garder la réponse centrée sur String.hashCode (). Object.hashCode () n'est absolument pas pertinent dans le contexte de cette question.

128
knorv

Je peux voir cette documentation aussi loin que Java 1.2.

Bien qu'il soit vrai que en général vous ne devriez pas compter sur une implémentation de code de hachage qui reste identique, il existe maintenant un comportement documenté pour Java.lang.String, donc le changer serait considéré comme une rupture des contrats existants.

Dans la mesure du possible, vous ne devriez pas vous fier aux codes de hachage en conservant les mêmes versions, mais dans mon esprit Java.lang.String est un cas particulier simplement parce que l’algorithme a a été spécifié ... aussi longtemps que vous êtes prêt à abandonner la compatibilité avec les versions avant que l’algorithme ait été spécifié.

96
Jon Skeet

J'ai trouvé quelque chose à propos de JDK 1.0 et 1.1 et> = 1.2:

Dans JDK 1.0.x et 1.1.x, la fonction hashCode pour les chaînes longues fonctionnait en échantillonnant chaque nième caractère. Cela garantit que beaucoup de chaînes de hachage auront la même valeur, ce qui ralentira la recherche sur Hashtable. Dans JDK 1.2, la fonction a été améliorée pour multiplier par 31 le résultat obtenu, puis ajouter le caractère suivant dans l’ordre. C'est un peu plus lent, mais c'est beaucoup mieux pour éviter les collisions. Source: http://mindprod.com/jgloss/hashcode.html

Quelque chose de différent, parce que vous semblez avoir besoin d'un numéro: que diriez-vous d'utiliser CRC32 ou MD5 au lieu de hashcode et que vous êtes prêt à partir - pas de discussion ni de souci du tout ...

18
ReneS

Vous ne devez pas compter sur un code de hachage égal à une valeur spécifique. Juste que cela va retourner des résultats cohérents dans la même exécution. Les documents de l'API disent ce qui suit:

Le contrat général de hashCode est:

  • Chaque fois qu'il est appelé plus d'une fois sur le même objet lors de l'exécution d'une application Java, la méthode hashCode doit systématiquement renvoyer le même entier, à condition qu'aucune information utilisée dans les comparaisons égales de l'objet ne soit modifiée Il n’est pas nécessaire que cet entier reste cohérent d’une exécution d’une application à une autre exécution de la même application.

EDIT Étant donné que le javadoc de String.hashCode () spécifie comment le code de hachage de String est calculé, toute violation de celle-ci violerait la spécification de l'API publique.

8
Martin OConnor

Comme indiqué ci-dessus, vous ne devez généralement pas compter sur le code de hachage d'une classe qui reste la même. Notez que même les exécutions ultérieures de la même application sur la même machine virtuelle peuvent produire différentes valeurs de hachage. Autant que je sache, la fonction de hachage de la JVM Sun calcule le même hachage à chaque exécution, mais ce n'est pas garanti.

Notez que ce n'est pas théorique. La fonction de hachage pour Java.lang.String a été modifiée dans JDK1.2 (l'ancien hachage avait des problèmes avec les chaînes hiérarchiques telles que les URL ou les noms de fichiers, car il avait tendance à produire le même hachage pour les chaînes qui ne différaient que à la fin).

Java.lang.String est un cas particulier, car l'algorithme de son hashCode () est (maintenant) documenté, vous pouvez donc probablement vous y fier. Je considère toujours que c'est une mauvaise pratique. Si vous avez besoin d'un algorithme de hachage avec des propriétés spéciales documentées, écrivez-en un :-).

4
sleske

Un autre problème (!) À prendre en compte est le possible changement de mise en œuvre entre les versions antérieures/tardives de Java. Je ne crois pas que les détails de la mise en œuvre soient gravés dans la pierre et donc potentiellement une mise à niveau vers une version future Java) pourrait poser problème.

En bout de ligne, je ne compterais pas sur la mise en œuvre de hashCode().

Peut-être que vous pouvez mettre en évidence le problème que vous essayez réellement de résoudre en utilisant ce mécanisme, et cela mettra en évidence une approche plus appropriée.

3
Brian Agnew

Juste pour répondre à votre question et ne pas continuer les discussions. L'implémentation Apache Harmony JDK semble utiliser un algorithme différent, du moins, il semble totalement différent:

Sun JDK

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

Apache Harmony

public int hashCode() {
    if (hashCode == 0) {
        int hash = 0, multiplier = 1;
        for (int i = offset + count - 1; i >= offset; i--) {
            hash += value[i] * multiplier;
            int shifted = multiplier << 5;
            multiplier = shifted - multiplier;
        }
        hashCode = hash;
    }
    return hashCode;
}

N'hésitez pas à le vérifier vous-même ...

2
ReneS

Si vous êtes préoccupé par les modifications et éventuellement par les ordinateurs virtuels, copiez simplement l’implémentation de hashcode existante dans votre propre classe d’utilitaire et utilisez-la pour générer vos hashcodes.

2
Sam Barnum

Le hashcode sera calculé sur la base des valeurs ASCII des caractères de la chaîne).

Ceci est l'implémentation dans la classe String est la suivante

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        hash = h = isLatin1() ? StringLatin1.hashCode(value)
                              : StringUTF16.hashCode(value);
    }
    return h;
}

Les collisions dans le hashcode sont inévitables. Par exemple, les chaînes "Ea" et "FB" donnent le même hashcode que 2236

0
Lourdes