Ce qui est un bon premier pour hashcode de calcul?
Eclipse 3.5 a une caractéristique très intéressante pour générer Java hashCode() de fonctions. Il pourrait générer, par exemple (légèrement raccourcie:)
class HashTest {
int i;
int j;
public int hashCode() {
final int prime = 31;
int result = prime + i;
result = prime * result + j;
return result;
}
}
(Si vous avez plus d'attributs dans la classe, result = prime * result + attribute.hashCode();
est répété pour chaque attribut supplémentaire. Pour ints .hashCode() peut être omis.)
Cela semble bien, mais pour le choix 31 pour le premier. C'est probablement à partir de la hashCode de la mise en œuvre de Java String, qui a été utilisé pour des raisons de performances qui ont disparu depuis longtemps après l'introduction de matériel de multiplication. Ici, vous avez beaucoup de hashcode collisions pour les petites valeurs de i et de j: par exemple (0,0) et (-1,31) ont la même valeur. Je pense que c'est une Mauvaise Chose(TM), depuis les petites valeurs se produisent souvent. Pour La Chaîne.hashCode vous trouverez également de nombreuses chaînes courtes avec le même hashcode, par exemple "Ca" et "DB". Si vous prenez une grande le premier, ce problème disparaît si vous choisissez le premier à droite.
Donc ma question: qu'est ce qu'un bon premier choisir? Quels critères avez-vous d'appliquer pour le trouver?
Il ne s'agit que d'une question d'ordre général - donc, je ne veux pas donner une gamme de i et j. Mais je suppose que, dans la plupart des applications relativement petites valeurs se produisent plus souvent que les grandes valeurs. (Si vous avez de grandes valeurs, le choix de la prime est probablement sans importance.) Il ne pourrait pas faire beaucoup de différence, mais un meilleur choix est un moyen facile et évident pour améliorer cette - alors, pourquoi ne pas le faire? Commons lang HashCodeBuilder suggère aussi curieusement petites valeurs.
(Clarification: c'est pas un double de Pourquoi Java est hashCode() en Chaîne de caractères utilisez 31 comme un multiplicateur? depuis que ma question n'est pas concerné par l'histoire de la 31 dans le JDK, mais sur ce que serait une meilleure valeur dans le nouveau code en utilisant le même modèle de base. Aucune des réponses, essayer de répondre à cela.)
- 31 est toujours bon qu'il ne s'agit pas nécessairement de chargement d'une constante. Sur un processeur ARM (au moins celle qui est utilisée par environ 99.9997% des téléphones mobiles)
*31
ne peut être en une seule instruction. Dans la réalité, n'importe quel nombre impair si le premier ou qui n'est pas bonne suffisant. - Je pensais à des programmes de bureau, où il n'a pas d'importance si vous choisissez 31 ou 1327144003. Curieusement, sur ma machine multipliant avec 31 est en fait un peu plus lent - sans doute une optimisation est mal passé. 😎
- Les nombres premiers de la forme
p = (2^n-1)
se prêtent à l'optimisation dex * p = (p << n) - p
qui le compilateur le fait habituellement. De Joshua Bloch, Efficace Java, Chapitre 3, Article 9. DONC, la question stackoverflow.com/questions/299304/... - et de se multiplier avec les integer <128 ont un élan supplémentaire à la jvm..
2^n-1
, le premier, petite .. ce qui donne à 31. - Comme je l'ai dit, pour le courant des machines de bureau, cela ne semble pas être une optimisation plus - le temps est le même. Pire encore: sur ma machine multipliant avec 31 a été un peu plus lente, peut-être que la JVM essayé pour "optimiser" par le calcul de x << 5 - x, et c'est effectivement plus lent que d'utiliser le matériel multiplicateur.
- Sur i86, il y a une différence, car il y a un mode pour un seul octet immédiate opérande. Vous obtenez une courte instruction et d'un indice de référence que j'ai écrit il y a des années, il a été un peu plus rapide.
- Veuillez noter que ceci est très différent de [Pourquoi Java est hashCode() en Chaîne de caractères utilisez 31 comme un multiplicateur?][1] puisque ce n'est pas à propos de l'histoire de l'31, mais sur ce que serait un meilleur choix au lieu d'utiliser 31, sans l'aide d'autres bibliothèques ou entièrement différentes méthodes de calcul de tables de hachage. Aucune des réponses adresses. [1]: stackoverflow.com/questions/299304/...
Vous devez vous connecter pour publier un commentaire.
Je recommande d'utiliser 92821. Voici pourquoi.
Pour donner une vraie réponse à cette question, vous devez savoir quelque chose sur les valeurs possibles de
i
etj
. La seule chose à laquelle je pense, en général, est, dans de nombreux cas, les petites valeurs sera plus commune que les grandes valeurs. (Les chances de 15 apparaissant comme une valeur dans votre programme sont beaucoup mieux que, disons, 438281923.) Il semble donc une bonne idée de faire le moindre hashcode de la collision la plus grande possible par choisir un nombre premier. Pour 31 ce plutôt mauvais - déjà pouri=-1
etj=31
vous avez la même valeur de hachage comme pouri=0
etj=0
.Depuis ce qui est intéressant, j'ai écrit un petit programme qui recherche l'ensemble de l'int gamme pour le meilleur premier dans ce sens. C'est, pour chacun, le premier j'ai cherché pour la valeur minimale de
Math.abs(i) + Math.abs(j)
sur toutes les valeurs dei,j
qui ont le même hashcode comme0,0
, puis a pris le premier où cette valeur minimale est la plus grande possible.Roulement de tambour: le meilleur premier dans ce sens est 486187739 (avec le plus petit collision
i=-25486, j=67194
). Presque aussi bon et beaucoup plus facile à retenir est 92821 avec la plus petite collisioni=-46272 and j=46016
.Si vous donnez des "petits" un autre sens et que vous voulez être au minimum de
Math.sqrt(i*i+j*j)
pour la collision la plus grande possible, les résultats sont un peu différents: le mieux serait 1322837333 aveci=-6815 and j=70091
, mais mon préféré 92821 (la plus petite collision-46272,46016
) est à nouveau presque aussi bonne que la meilleure valeur.Je dois reconnaître que c'est tout à fait discutable de savoir si ces calcul beaucoup de sens dans la pratique. Mais je pense que la prise de 92821 en tant que premier fait beaucoup plus de sens que 31, sauf si vous avez de bonnes raisons de ne pas.
newArrayList("a", "bc").hashCode() == newArrayList("ab", "c").hashCode()
(mon exemple mayn fonctionne pas, mais quelque chose de similaire n').n
une bonne constante, de l'utiliser partout dans le monde peuvent pas être de droite car elle génère des conflits inutiles de façon systématique. À l'aide d'une constante pour cordes et un autre pour des listes est à mon humble avis mieux.hashCode
zéro pour toute nouvelle Carte.Entrée avec égalité de clés et de valeurs, etc.) alors même que de 0,1% étant sans doute digne d'amélioration.En fait, si vous prenez un premier tellement grand qu'elle touche de près à la
INT_MAX
, vous avez le même problème à cause de l'arithmétique modulo. Si vous vous attendez à de hachage pour la plupart des chaînes de longueur 2, peut-être un premier près de la racine carrée deINT_MAX
serait mieux, si les chaînes vous de hachage sont de plus il n'a pas tellement d'importance et les collisions sont inévitables de toute façon...Collisions peut-être pas un gros problème... L'objectif principal de la table de hachage est d'éviter de l'aide est égal à 1:1 comparaisons.
Si vous avez une implémentation où égale est "généralement" très bon marché pour les objets qui ont percuté hashs, alors ce n'est pas un problème (à tous).
En fin de compte, quelle est la meilleure façon de hachage dépend de ce que vous comparez. Dans le cas d'un int paire (comme dans votre exemple), en utilisant les opérateurs au niveau du bit pourrait être suffisante (comme à l'aide de & ou ^).
Vous devez définir votre gamme de i et j. Vous pouvez utiliser un nombre premier pour les deux.
Moi je préfère 7243. Suffisamment grande pour éviter collissions avec de petits nombres. Ne déborde pas des petits nombres rapidement.
Je veux juste souligner que le hashcode n'a rien à voir avec le premier.
Dans le JDK de mise en œuvre
J'ai découvert que si vous remplacez 31 avec 27, les résultats sont très similaires.
String#intern
.m = 101*103*107*109
est une catastrophe pour une table de hachage de taille103
(mais personne ne l'utilise à de telles tailles). Pour une puissance de deux de la taille de la table elle est probablement beaucoup mieux que31
. Est-il donc probablement pour un tableau de la taille de la co-prime àm
.