web-dev-qa-db-fra.com

Implémentation d'un HashMap

Comment créer un Hashmap en C à partir de zéro? Quels seraient les paramètres pris en considération et comment testeriez-vous la table de hachage pour savoir si elle est bonne? Comme dans ce qui serait des cas de test de référence que vous devez exécuter avant de dire que votre carte de hachage est terminée.

42
Thunderboltz

Eh bien, si vous connaissez les principes de base, cela ne devrait pas être trop difficile.

En général, vous créez un tableau appelé "compartiments" qui contient la clé et la valeur, avec un pointeur facultatif pour créer une liste liée.

Lorsque vous accédez à la table de hachage avec une clé, vous traitez la clé avec une fonction de hachage personnalisée qui renverra un entier. Vous prenez ensuite le module du résultat et c'est l'emplacement de votre index de tableau ou "bucket". Ensuite, vous vérifiez la clé non hachée avec la clé stockée, et si elle correspond, alors vous avez trouvé le bon endroit.

Sinon, vous avez eu une "collision" et devez parcourir la liste chaînée et comparer les clés jusqu'à ce que vous correspondiez. (notez que certaines implémentations utilisent un arbre binaire au lieu d'une liste chaînée pour les collisions).

Découvrez cette implémentation rapide de table de hachage:

https://attractivechaos.wordpress.com/2009/09/29/khash-h/

56
Unknown

La meilleure approche dépend de la distribution des clés attendue et du nombre de collisions. Si relativement peu de collisions sont attendues, peu importe la méthode utilisée. Si de nombreuses collisions sont attendues, celle à utiliser dépend du coût de ressassement ou de vérification par rapport à la manipulation de la structure de données de compartiment extensible.

Mais voici un exemple de code source de An Hashmap Implementation in C

5
TStamper

L'objectif principal d'une table de hachage est de stocker un ensemble de données et de fournir des recherches en temps quasi constant sur celui-ci à l'aide d'une clé unique. Il existe deux styles courants d'implémentation de la table de hachage:

  • Chaînage séparé: un avec un tableau de compartiments (listes chaînées)
  • Adressage ouvert: un seul tableau alloué avec un espace supplémentaire afin que les collisions d'index puissent être résolues en plaçant l'entrée dans un emplacement adjacent.

Un chaînage séparé est préférable si la table de hachage peut avoir une mauvaise fonction de hachage, il n'est pas souhaitable de préallouer du stockage pour des emplacements potentiellement inutilisés, ou les entrées peuvent avoir une taille variable. Ce type de hashmap peut continuer à fonctionner relativement efficacement même lorsque le facteur de charge dépasse 1,0. De toute évidence, une mémoire supplémentaire est requise dans chaque entrée pour stocker les pointeurs de liste liée.

Les Hashmaps utilisant l'adressage ouvert ont des avantages potentiels en termes de performances lorsque le facteur de charge est maintenu en dessous d'un certain seuil (généralement environ 0,7) et qu'une fonction de hachage raisonnablement bonne est utilisée. En effet, ils évitent les ratés potentiels du cache et de nombreuses petites allocations de mémoire associées à une liste liée, et effectuent toutes les opérations dans un tableau contigu et préalloué. L'itération à travers tous les éléments est également moins chère. Le hic est que les hashmaps utilisant l'adressage ouvert doivent être réalloués à une plus grande taille et retravaillés pour maintenir un facteur de charge idéal, ou ils font face à une pénalité de performance significative. Il est impossible que leur facteur de charge dépasse 1,0.

Certaines mesures de performances clés à évaluer lors de la création d'une carte de hachage incluent:

  • Facteur de charge maximum
  • Nombre moyen de collisions à l'insertion
  • Distribution des collisions: une distribution inégale (regroupement) pourrait indiquer une mauvaise fonction de hachage.
  • Temps relatif pour diverses opérations: mettre, obtenir, supprimer des entrées existantes et non existantes.

Voici une implémentation de hashmap flexible que j'ai faite. J'ai utilisé l'adressage ouvert et le sondage linéaire pour la résolution des collisions.

https://github.com/DavidLeeds/hashmap

3
Dave

Il existe d'autres mécanismes pour gérer le débordement que la simple liste liée à l'esprit des entrées de débordement qui, par exemple. gaspille beaucoup de mémoire.

Le mécanisme à utiliser dépend entre autres si vous pouvez choisir la fonction de hachage et en choisir plusieurs (pour implémenter par exemple le double hachage pour gérer les collisions); si vous prévoyez d'ajouter souvent des éléments ou si la carte est statique une fois remplie; si vous avez l'intention de supprimer des éléments ou non; ...

La meilleure façon de l'implémenter est de penser d'abord à tous ces paramètres, puis de ne pas le coder vous-même, mais de choisir une implémentation existante mature. Google a quelques bonnes implémentations - par exemple http://code.google.com/p/google-sparsehash/

1
HD.