Je sais que STL dispose d'une API HashMap, mais je ne trouve aucune documentation fiable et exhaustive contenant de bons exemples.
Tous les bons exemples seront appréciés.
La bibliothèque standard comprend les conteneurs de cartes ( std::map
et std::unordered_map
) commandés et non ordonnés. Dans une carte ordonnée, les éléments sont triés par la clé, insérés et l'accès se fait par O (log n) . Habituellement, la bibliothèque standard utilise en interne - arbres noirs rouges pour les cartes ordonnées. Mais ceci est juste un détail de mise en œuvre. Dans une carte non ordonnée, insérer et accéder est dans O (1). C'est juste un autre nom pour un hashtable.
Un exemple avec (commandé) std::map
:
#include <map>
#include <iostream>
#include <cassert>
int main(int argc, char **argv)
{
std::map<std::string, int> m;
m["hello"] = 23;
// check if key is present
if (m.find("world") != m.end())
std::cout << "map contains key world!\n";
// retrieve
std::cout << m["hello"] << '\n';
std::map<std::string, int>::iterator i = m.find("hello");
assert(i != m.end());
std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
return 0;
}
Sortie:
23 Touche: hello Valeur: 23
Si vous avez besoin de commander dans votre conteneur et que vous utilisez le runtime O (log n), utilisez simplement std::map
.
Sinon, si vous avez vraiment besoin d'une table de hachage (O (1) insert/access), consultez std::unordered_map
, qui a une API similaire à std::map
(par exemple, il vous suffit de rechercher et de remplacer map
par unordered_map
).
Le conteneur unordered_map
a été introduit avec C++ 11 standard revision. Ainsi, en fonction de votre compilateur, vous devez activer les fonctionnalités de C++ 11 (par exemple, lorsque vous utilisez GCC 4.8, vous devez ajouter -std=c++11
à CXXFLAGS).
Même avant la version C++ 11, GCC était pris en charge unordered_map
- dans l'espace de noms std::tr1
. Ainsi, pour les anciens compilateurs GCC, vous pouvez essayer de l’utiliser comme ceci:
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
Cela fait également partie de boost, c'est-à-dire que vous pouvez utiliser le boost-header correspondant pour une meilleure portabilité.
Un hash_map
est une version ancienne et non standardisée de ce que l'on appelle un unordered_map
(actuellement disponible dans le cadre de TR1 et qui sera inclus dans C++ 0x). Comme son nom l'indique, il diffère de std::map
principalement par le fait qu'il soit non ordonné - si, par exemple, vous parcourez une carte de begin()
à end()
, vous obtenez les éléments dans l'ordre par clé.1, mais si vous parcourez un unordered_map
de begin()
à end()
, vous obtenez les éléments dans un ordre plus ou moins arbitraire.
Un unordered_map
devrait normalement avoir une complexité constante. En d'autres termes, une insertion, une recherche, etc., prend essentiellement un temps fixe, quel que soit le nombre d'éléments contenus dans la table. Un std::map
a une complexité logarithmique sur le nombre d'éléments stockés - ce qui signifie que le temps d'insertion ou de récupération d'un élément augmente, mais très lentement, à mesure que la carte s'agrandit. Par exemple, s'il faut 1 microseconde pour rechercher un élément sur 1 million, vous pouvez vous attendre à environ 2 microsecondes pour rechercher un élément sur 2 millions, 3 microsecondes pour un élément sur 4 millions, 4 microsecondes pour un élément sur 8 millions. articles, etc.
D'un point de vue pratique, ce n'est pas vraiment toute l'histoire. Par nature, une table de hachage simple a une taille fixe. L'adapter aux exigences de taille variable pour un conteneur à usage général est quelque peu non trivial. En conséquence, les opérations qui (potentiellement) grossissent ou rétrécissent le tableau (par exemple, insertion et suppression) sont souvent relativement lentes. Les recherches, qui ne peuvent pas changer la taille de la table, sont généralement beaucoup plus rapides. En conséquence, la plupart des tables à base de hachage ont tendance à être optimales lorsque vous effectuez beaucoup de recherches et que vous insérez relativement peu de fois des suppressions et des suppressions. Dans les cas où vous insérez beaucoup de données, parcourez la table une fois pour récupérer les résultats (par exemple, en comptant le nombre de mots uniques dans un fichier), il est probable qu'un std::map
sera aussi rapide et peut-être même plus rapide.
1 Lorsque l'ordre est défini par le troisième paramètre de modèle lorsque vous créez la carte, std::less<T>
par défaut.
Voici un exemple plus complet et plus flexible qui n'omette pas les éléments nécessaires pour générer des erreurs de compilation:
#include <iostream>
#include <unordered_map>
class Hashtable {
std::unordered_map<const void *, const void *> htmap;
public:
void put(const void *key, const void *value) {
htmap[key] = value;
}
const void *get(const void *key) {
return htmap[key];
}
};
int main() {
Hashtable ht;
ht.put("Bob", "Dylan");
int one = 1;
ht.put("one", &one);
std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}
Ce n'est toujours pas particulièrement utile pour les clés, à moins qu'elles ne soient prédéfinies en tant que pointeurs, car une valeur correspondante ne fera pas l'affaire! (Cependant, étant donné que j'utilise normalement des chaînes pour les clés, le fait de remplacer "const void *" par "chaîne" dans la déclaration de la clé devrait résoudre ce problème.)
Comment utiliser une classe personnalisée et une fonction de hachage avec unordered_map
Cette réponse indique: C++ unordered_map en utilisant un type de classe personnalisé comme clé
Extrait: égalité:
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
Fonction de hachage:
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
}