Pourquoi Chaîne.hashCode() en Java, ont de nombreux conflits?
Pourquoi Chaîne.hashcode() ont tant de conflits?
Je suis en train de lire la Chaîne de caractères.hashCode() en jdk1.6, ci-dessous la des codes de
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;
}
Cela ressemble assez déroutant pour moi car il a de nombreux conflits; bien qu'il n'est pas nécessaire d'être unique (nous pouvons toujours compter sur la equals()), mais moins de conflits signifie de meilleures performances sans visiter les entrées dans une liste liée.
Supposons que nous avons deux personnages, alors, aussi longtemps que nous pouvons trouver deux chaînes de caractères qui correspondent au-dessous de l'équation, alors nous aurons le même hashcode()
a * 31 +b = c * 31 +d
Il sera facile de conclure que (a-c) * 31 = d-b
prenons un exemple simple, c'est de faire un-c = 1 et d-b = 31;
j'ai donc écrit ci-dessous les codes de test simple
public void testHash() {
System.out.println("A:" + (int)'A');
System.out.println("B:" + (int)'B');
System.out.println("a:" + (int)'a');
System.out.println("Aa".hashCode() + "," + "BB".hashCode());
System.out.println("Ba".hashCode() + "," + "CB".hashCode());
System.out.println("Ca".hashCode() + "," + "DB".hashCode());
System.out.println("Da".hashCode() + "," + "EB".hashCode());
}
il permet d'imprimer ci-dessous les résultats qui signifie que toutes les chaînes ont le même hashcode(), et il est facile de le faire dans une boucle.
A:65
B:66
a:97
2112,2112
2143,2143
2174,2174
2205,2205
pire encore, supposons que nous avons 4 caractères dans la chaîne, en fonction de l'algorithme, supposons que les 2 premiers caractères produire de l'a2, le 2ème 2 caractères produire b2;
le hashcode sera toujours a2 * 31^2 + b2
ainsi, avec a2 et b2 de l'égalité entre les 2 chaînes de caractères, nous allons obtenir plus de chaînes avec hashcode() de conflit.
de tels exemples sont "AaAa", "BBBB" et ainsi de suite;
ensuite, nous aurons 6 caractères 8 caractères......
supposons que la plupart du temps on utilise des caractères ascii tableau dans une chaîne de caractères qui sera utilisée dans une table de hachage ou de table de hachage, puis le choisi le premier numéro 31 ici est vraiment trop petite;
une solution facile est d'utiliser un plus grand nombre premier (heureusement, 257 est un nombre premier), ce qui peut éviter ce conflit. bien sûr, choisir un trop grand nombre sera la cause renvoyée int valeur d'être débordé si la chaîne est très longue, mais je suppose que la plupart du temps la chaîne utilisée comme une clé n'est pas que les grandes?
bien sûr, il pouvait toujours retourner une valeur de type long pour éviter cela.
ci-dessous est ma version modifiée de betterhash (), qui peut résoudre de tels conflits facilement
en exécutant les codes, il permet d'imprimer les valeurs ci-dessous, qui est efficace pour résoudre ce problème.
16802,17028
17059,17285
17316,17542
17573,17799
mais pourquoi jdk ne pas le fixer? thx.
@Test
public void testBetterhash() {
System.out.println(betterHash("Aa") + "," + betterHash("BB"));
System.out.println(betterHash("Ba") + "," + betterHash("CB"));
System.out.println(betterHash("Ca") + "," + betterHash("DB"));
System.out.println(betterHash("Da") + "," + betterHash("EB"));
}
public static int betterHash(String s) {
int h = 0;
int len = s.length();
for (int i = 0; i < len; i++) {
h = 257*h + s.charAt(i);
}
return h;
}
- Si vous souhaitez obtenir des pinailleurs, il y a théoriquement un nombre infini de collisions, peu importe la force de votre hash.
- Je crois que vous vouliez dire des clés dans une carte au lieu d'entrées dans une LinkedList.
- Si vous voulez demander votre hash est "mieux", vous devriez réellement de la procédure de hachage, dire, chaque chaîne de caractères (au moins ascii) de moins de 8 ou autant de personnages, puis de comparer le nombre de collisions par rapport à l'original. J'imagine que 8 caractères est beaucoup plus proche de la moyenne de la taille de la clé de 4 (si ce n'est pas encore sur le bas côté!).
- En effet, hetaoblog dit que la nouvelle valeur de hachage débordement si la clé est "très long"; en fait, il va le faire avec seulement 5 caractères.
- thx pour le signaler, je ne savais pas ce-- il suffit d'exécuter un test et ce qui se passe comme vous l'avez dit--
- J'ai peut-être un malentendu, mais votre meilleur hash solution est de remplacer un facteur constant avec l'autre (les deux premiers, de sorte qu'ils ont fondamentalement les mêmes propriétés mathématiques) et jugent qu'il est mieux, car il n'y a pas de collisions sur la base de 4 arbitraire choisi cordes? Que faire si je pick 4 arbitraire d'autres chaînes? Soudain, l'ancienne version est mieux encore.. Il y a de mieux hashcodes là-bas et que vous pouvez prouver mathématiquement qu'ils sont mieux, mais ils sont beaucoup plus complexe et plus lent.
- Je pars de ce commentaire comme une plainte pour ceux qui ont eu cela comme un non-constructive de la question. La question "Pourquoi la Chaîne.hashcode() ont tant de conflits?" est vraiment celui qui mérite discussion dans toute technologie Q&A. les Gens ont besoin de comprendre l'objectif, factuel réponse à cette question. Je pense aussi que les réponses à cette question ont fait du bon boulot pour répondre à l'objectif en question. Je pense aussi que l'affiche a fourni une belle solution à ce problème spécifique, il a fait face. Nous avons tous besoin d'écrire un domaine spécifique de fonctions de hachage, de temps à autre.
Vous devez vous connecter pour publier un commentaire.
Je viens de haché de 58 mille anglais mots (trouvé ici), à la fois minuscule et aussi avec la première lettre en majuscule . Savoir combien de collision? Deux: "Frères et sœurs" et "Téhéran" (une autre orthographe de "Téhéran").
Tout comme vous, j'ai pris un sous-domaine (dans mon cas, un probablement de l'un sur les cordes et analysé le hashCode le taux de collision pour elle, et l'a trouvé pour être exemplaire. Qui est-à-dire que votre arbitraires sous-domaine du possible des chaînes est un meilleur choix pour optimiser pour que le mien?
Les gens qui ont écrit cette classe devait le faire en sachant qu'ils ne pouvaient pas prévoir (ni, par conséquent, d'optimiser) le sous-domaine dans lequel leurs utilisateurs d'utiliser des Chaînes de caractères comme des clés. Donc, ils ont choisi une fonction de hachage qui distribue uniformément sur la ensemble domaine de chaînes.
Si vous êtes intéressé, voici mon code (il utilise Goyave):
Modifier
Par ailleurs, si vous êtes curieux de le même test avec votre fonction de hachage a 13 collisions par rapport à
String.hashCode
's 1.Je suis désolé, mais nous avons besoin de jeter de l'eau froide sur cette idée.
Votre analyse est trop simpliste. Vous semblez avoir de la cerise cueillies à un sous-ensemble de Chaînes qui est conçu pour prouver votre point. Ce n'est pas la preuve que le nombre de collisions est (statistiquement) plus élevé que prévu sur l'ensemble du domaine de toutes les chaînes.
Personne dans leur bon esprit attendre Chaîne.hashCode très libre de collision. Il n'est tout simplement pas conçus dans cet esprit. (Si vous voulez très libre de collision de hachage, puis d'utiliser un algorithme de hachage cryptographique ... et en payer le coût.) Chaîne de caractères.hashCode() est conçu pour être assez bonne sur l'ensemble du domaine de toutes les Chaînes ... et rapide.
En supposant que vous pourriez état des arguments plus solides, ce n'est pas le lieu de le dire. Vous avez besoin de soulever cette question avec les personnes qui comptent - Oracle Java équipe d'ingénierie.
La Java équipe d'ingénierie vont peser les avantages d'un tel changement par rapport aux coûts de mise en oeuvre, pour eux, et pour tous les autres utilisateurs de Java. Le dernier point est probablement assez pour tuer cette idée de pierre morte.
("Très libre de collision de hachage", est une idée ou le terme que j'ai tirée de l'air pour les fins de cette réponse. Désolé. Toutefois, l'essentiel est que la probabilité d'un hashcode de collision pour les 2 chaînes devraient être indépendant de la manière dont ils sont liés. Donc, par exemple. "AA" et "bz" sont liées par le fait d'avoir la même longueur. Évidemment, cette idée a besoin de plus de pensée. Et il est aussi évident que "la parenté", dans le sens dont je parle n'est pas mesurable ... un peu comme La Complexité De Kolmogorov.)
Les Collisions sont inévitables lors de hachage. Le
hashCode()
méthode retourne un entier qui est utilisé comme un index dans un tableau qui est un seau pour tous les objets avec le même code de hachage. Leequals(Object)
méthode est utilisée pour comparer l'objet cible avec chacun dans le seau à identifier la correspondance exacte de l'objet, si elle existe.En fin de compte, la
hashCode()
méthode a juste besoin d'être rapide et pas trop faible (c'est à dire trop de collisions), où trop faible est assez floue métrique.Il est assez efficace, mais aussi très simple. Tout en permettant une baisse des cas (ASCII) mots de six lettres ou tous les numéros à six chiffres ont un unique hashCode(). c'est à dire le hashCode est comme une base de 31 nombre. À l'aide d'un plus grand nombre a ses propres problèmes. 257 facteur de quitter tous les 8 bits pas particulièrement aléatoire que tous les caractères ASCII 0 top de bit. Un plus grand facteur de résultat en double exemplaire hashcodes pour cinq et six chiffres/lettres.
Ce qui est peut-être le plus gros problème si vous ne pouvez pas modifier l'algorithme de hachage. Quelle que soit l'approche que vous prenez, il peut y avoir des cas où c'est un très mauvais choix et susceptible d'être sous-optimale pour votre cas d'utilisation.
Peut-être le plus gros problème est de déni de service attaques faisant des cas pathologiques, normalement très rare assez commun. Par exemple, une façon d'attaquer un serveur web est de remplir un cache avec les touches de tous avec le même hashCode par exemple 0, qui est calculé à chaque fois. Cette cause HashMap de dégénérer en une linkedlist.
Un moyen simple de contourner cela est de faire de l'algorithme de hachage inconnu, ce qui pourrait changer. Comme ses peuplements, le mieux est d'utiliser un TreeMap (qui prend en charge personnalisée de Comparaison, si le défaut serait bien dans ce cas)