Est-il un avantage de l'utilisation de la carte sur unordered_map en cas de trivial clés?
Une récente parler unordered_map
en C++ me suis rendu compte que je devrais utiliser unordered_map
pour la plupart des cas où j'ai utilisé map
avant, en raison de l'efficacité de la recherche ( amorti O(1) vs O(log n) ). La plupart du temps j'utilise une carte, j'utilise soit int
ou std::string
comme le type de la clé; c'est pourquoi je n'ai pas de problèmes avec la définition de la fonction de hachage. Plus j'y pensais, plus je me rends compte que je ne peux pas trouver une raison de l'utilisation d'un std::map
sur une std::unordered_map
dans le cas de clés avec les types simples-j'ai pris un coup d'oeil au niveau des interfaces, et n'a pas trouvé de différences significatives qui aurait un impact sur mon code.
D'où la question: est-il vraiment de raison d'utiliser std::map
sur std::unordered map
dans le cas de types simples comme les int
et std::string
?
Je me demande à partir d'un strict point de vue programmation, je sais qu'il n'est pas pleinement considéré comme la norme, et que cela peut poser des problèmes avec le portage.
Aussi, j'attends que l'une des bonnes réponses peut être "il est plus efficace pour les petits ensembles de données" en raison d'une moindre frais généraux (est-ce vrai?) - donc j'aimerais restreindre la question au cas où le montant des touches est non-trivial (>1 024).
Edit: euh, j'ai oublié l'évidence (merci GMan!) -- oui, les cartes sont commandés, bien sûr, je sais qu', et je suis à la recherche pour d'autres raisons.
- J'aime poser cette question dans les entretiens: "Quand est-tri rapide mieux que la bulle de tri?" La réponse à la question donne un aperçu de l'application pratique de la théorie de la complexité et non de la simple noir et blanc des déclarations telles que O(1) est meilleure que O(n) ou O(k) est équivalent à O(logn) etc....
- Je pense que tu voulais dire "quand est-bulle-sort mieux que le tri rapide" 😛
- Serait un pointeur intelligent être une banale clé?
- Voici l'un des cas dans lesquels la carte est avantageuse: stackoverflow.com/questions/51964419/...
Vous devez vous connecter pour publier un commentaire.
N'oubliez pas que
map
garde ses éléments commandés. Si vous ne pouvez pas abandonner, évidemment, vous ne pouvez pas utiliserunordered_map
.Autre chose à garder à l'esprit est que
unordered_map
généralement utilise plus de mémoire.map
a juste quelques maison de maintien de pointeurs, et de la mémoire pour chaque objet. A l'inverse,unordered_map
a un grand tableau (que l'on peut obtenir assez grandes dans certaines implémentations), puis de la mémoire supplémentaire pour chaque objet. Si vous avez besoin d'être en mémoire consciente,map
doit prouver mieux, parce qu'il manque le grand tableau.Donc, si vous avez besoin de la pure recherche de la récupération, je dirais
unordered_map
est le chemin à parcourir. Mais il y a toujours des compromis, et si vous ne pouvez pas se le permettre, alors vous ne pouvez pas l'utiliser.Juste à partir de l'expérience personnelle, j'ai trouvé une énorme amélioration dans les performances (mesurées, bien sûr) lors de l'utilisation de
unordered_map
au lieu demap
au sein d'une grande entité (look-up table.D'autre part, j'ai trouvé que c'était beaucoup plus lente, à plusieurs reprises, l'insertion et la suppression d'éléments. Il est idéal pour un relativement statique de la collection d'éléments, mais si vous êtes en train de faire des tonnes d'insertions et de suppressions de hachage + écopage semble ajouter jusqu'à. (À noter que cela a été pendant de nombreuses itérations.)
unordered_map
et de la réserve qu'au début - avez-vous encore de payer une pénalité de nombreuses insertions? Dire que vous êtes seulement l'insertion d'une fois lorsque vous avez créé la table de recherche - et plus tard seulement lire.std::map
est mis en œuvreSi vous voulez comparer la vitesse de votre
std::map
etstd::unordered_map
implémentations, vous pouvez utiliser Google sparsehash projet qui a un time_hash_map programme à temps eux. Par exemple, avec gcc 4.4.2 sur un système Linux x86_64J'avais echo à peu près le même point de GMan fait: selon le type d'utilisation,
std::map
peut être (et est souvent) plus rapide questd::tr1::unordered_map
(à l'aide de la mise en œuvre inclus dans VS 2008 SP1).Il y a quelques facteurs à garder à l'esprit. Par exemple, dans
std::map
, vous êtes en comparant les clés, ce qui signifie que vous ne regardez jamais à assez de début d'une clé de la distinction entre droite et gauche sous-branches de l'arbre. Dans mon expérience, presque la seule fois que vous regardez l'intégralité de la clé est de savoir si vous êtes en utilisant quelque chose comme int que vous pouvez comparer en une seule instruction. Avec un plus typique clé de type std::string, vous compare souvent à seulement quelques caractères ou plus.Un décent fonction de hachage, par contraste, regarde toujours l' ensemble clé. OIE, même si la table de recherche est la constante de la complexité, de la valeur de hachage lui-même a peu près linéaire de la complexité (bien que sur la longueur de la clé, pas le nombre d'éléments). Avec de longues chaînes de caractères comme des clés, un
std::map
peut terminer une recherche avant ununordered_map
serait même commencer sa recherche.Deuxième, bien qu'il existe plusieurs méthodes de redimensionnement des tables de hachage, la plupart d'entre eux sont assez lent-au point que, à moins que les recherches sont considérablement plus fréquentes que les insertions et les suppressions, std::map sera souvent plus rapide que
std::unordered_map
.Bien sûr, comme je l'ai mentionné dans le commentaire relatif à votre question précédente, vous pouvez également utiliser une table des arbres. Cela a des avantages et des inconvénients. D'une part, elle limite le pire des cas à celle d'un arbre. Il permet aussi rapide d'insertion et de suppression, parce que (au moins, quand je l'ai fait, j'ai utilisé une taille fixe de la table. L'élimination de tous redimensionnement d'un tableau vous permet de garder votre table de hachage beaucoup plus simple et généralement plus rapide.
Un autre point: les exigences pour le hachage et des arbres, les cartes sont différentes. Le hachage nécessite à l'évidence une fonction de hachage, et une comparaison d'égalité, où commandé des cartes nécessitent moins de comparaison. Bien sûr, les hybrides, je l'ai mentionné nécessite à la fois. Bien sûr, pour le cas de l'utilisation d'une chaîne de caractères comme la clé, ce n'est pas vraiment un problème, mais certains types de touches de fonction de commande de mieux que de hachage (ou vice versa).
dynamic hashing
techniques, qui consistent à avoir une période de transition où chaque fois que vous insérez un élément, vous aussi resucéek
d'autres éléments. Bien sûr, cela signifie que, pendant la transition, vous avez à la recherche de 2 tables différentes...unordered_map
besoin de confirmer un hash match avec une comparaison complète, donc tout dépend de ce que les parties du processus de recherche, vous êtes contrastées.J'ai été intrigué par la réponse de @Jerry Cercueil, ce qui suggère que la commande de la carte serait présentent des hausses de performances sur de longues cordes, après une période d'expérimentation (qui peut être téléchargé à partir de pastebin), j'ai trouvé que cela ne semble vrai pour les collections de chaînes aléatoires, lorsque la carte est initialisée avec un triées dictionnaire (qui contiennent des mots avec des quantités considérables de préfixe de chevauchement), cette règle se décompose, probablement en raison de l'augmentation de la profondeur de l'arbre nécessaire pour récupérer la valeur. Les résultats sont présentés ci-dessous, le 1er numéro de la colonne est l'heure d'insertion, la 2e est de récupération.
Je voudrais juste souligner que... il y a de nombreux types de
unordered_map
s.Chercher le Article De Wikipedia sur le hachage de la carte. En fonction de la mise en œuvre a été utilisé, les caractéristiques en terme de recherche, d'insertion et de suppression peuvent varier de façon très significative.
Et c'est ce qui m'inquiète le plus avec l'ajout de
unordered_map
de la STL: ils devront choisir une mise en oeuvre particulière que je doute qu'ils vont aller en bas de laPolicy
de la route, et nous allons donc être coincé avec une mise en œuvre pour la moyenne de l'utiliser et rien pour les autres cas...Par exemple, certains de hachage, les cartes ont linéaire ressasser, où, au lieu de ressasser tout le hachage de la carte à la fois, une partie est réchauffé à chaque insertion, ce qui permet d'amortir le coût.
Un autre exemple: certains de hachage des cartes utiliser une simple liste de nœuds pour un seau, d'autres l'utilisation d'une carte, d'autres n'utilisez pas de nœuds, mais trouver le plus proche de logement et, enfin, certains utilisent une liste de nœuds, mais de la réorganiser, de sorte que le dernier élément est accessible à l'avant (comme une mise en cache des chose).
Donc pour le moment j'ai tendance à préférer le
std::map
ou peut-être unloki::AssocVector
(pour les surgelés ensembles de données).Ne m'obtenez pas le mal, je voudrais utiliser la
std::unordered_map
et je peut, dans l'avenir, mais il est difficile de "faire confiance" à la portabilité d'un tel conteneur lorsque vous pensez à toutes les façons de la mettre en œuvre et les différentes prestations qui résultent de cette.Différences importantes qui n'ont pas vraiment été suffisamment évoqué ici:
map
garde des itérateurs pour tous les éléments stables, en C++17 vous pouvez même déplacer des éléments d'unemap
à l'autre sans pour autant invalider les itérateurs pour eux (et si elles sont appliquées correctement, sans aucune affectation éventuelle).map
les horaires pour de simples opérations sont généralement plus cohérent puisqu'ils n'ont jamais besoin d'importantes allocations.unordered_map
à l'aide destd::hash
implémenté dans la bibliothèque libstdc++ est vulnérable à DoS si nourris avec douteuses en entrée (il utilise MurmurHash2 avec une constante de semences non pas que les semis qui serait vraiment utile, voir https://emboss.github.io/blog/2012/12/14/breaking-murmur-hash-flooding-dos-reloaded/).Tables de hachage ont plus constantes que carte commune implémentations, qui deviennent significatifs pour les petits contenants. La taille maximum est de 10, 100, ou peut-être même 1000 ou plus? Les constantes sont toujours les mêmes, mais en O(log n) est proche de O(k). (Rappelez-vous logarithmique de la complexité est encore vraiment bon.)
Ce qui fait une bonne fonction de hachage dépend de vos données caractéristiques; donc, si je n'ai pas de plan sur la recherche à un personnalisé fonction de hachage (mais peut certainement changer d'avis plus tard, et facilement depuis que je typedef sacrément proche de tout) et même si les valeurs par défaut sont choisis pour effectuer décemment pendant de nombreuses sources de données, je trouve la nature ordonnée de la carte à être assez d'une aide au départ que j'ai encore défaut à la carte plutôt que d'une table de hachage dans ce cas.
Plus de cette façon, vous n'avez pas à même de penser à l'écriture d'une fonction de hachage pour d'autres (généralement de type défini par l'utilisateur) les types, et il suffit d'écrire op< (que vous voulez de toute façon).
map
et l'un desunordered_map
, avec certaines plate-forme et de certains taille du cache, et de faire une analyse complexe. 😛Raisons ont été données dans d'autres réponses; ici est une autre.
std::map (arbre binaire équilibré) opérations sont amortis O(log n) et le pire cas O(log n).
std::unordered_map (table de hachage) opérations sont amorti O(1) et au pire des cas O(n).
Comment cela se joue dans la pratique, c'est que la table de hachage "hoquet" à chaque fois dans un certain temps avec un O(n) opérations, qui peuvent ou peuvent ne pas être quelque chose que votre application peut tolérer. Si il ne peut pas le supporter, vous préférez std::map sur std::unordered_map.
J'ai fait un test récemment, ce qui a fait 50000 fusion&tri. Cela signifie que si la chaîne touches sont les mêmes, de fusionner la chaîne d'octets. Et le résultat final doit être triée. Donc, cela inclut un look pour chaque insertion.
Pour la
map
mise en œuvre, il faut 200 ms pour terminer le travail. Pour leunordered_map
+map
, il prend 70 ms pourunordered_map
d'insertion et de 80 ms pourmap
d'insertion. Si l'hybride de la mise en œuvre est de 50 ms plus rapide.Nous devrions réfléchir à deux fois avant d'utiliser le
map
. Si vous avez seulement besoin de données pour être triés dans le résultat final de votre programme de, une solution hybride peut-être mieux.Résumé
En supposant que la commande n'est pas important:
std::unordered_map
std::map
. C'est parce que les lectures sontO(log n)
.std::map
est la bonne option.std::unordered_map
.Contexte Historique
Dans la plupart des langues, non triées de la carte (aka le hachage en fonction des dictionnaires) sont de la map par défaut cependant, en C++, vous obtenez commandé la carte comme carte par défaut. Comment est-ce arrivé? Certaines personnes, à tort, supposons que C++ comité a pris cette décision dans leur unique sagesse, mais la vérité est, malheureusement, plus laid que ça.
Il est largement cru que le C++ est retrouvé avec ordonné la carte en tant que par défaut, car il n'y a pas trop de paramètres sur la façon dont ils peuvent être mis en œuvre. D'autre part, le hachage en fonction des implémentations a des tonnes de choses à raconter. Donc, pour éviter les blocages dans la normalisation ils viens le long de commandés carte. Autour de 2005, de nombreuses langues avaient déjà de bonnes implémentations de hachage en fonction de la mise en œuvre et il était donc plus facile pour le comité d'accepter de nouveaux
std::unordered_map
. Dans un monde parfait,std::map
aurait été non ordonnée et nous aurionsstd::ordered_map
comme type distinct.Performance
Ci-dessous deux graphiques parlent d'eux-mêmes (source):
Petit plus à l'ensemble de la ci-dessus:
Mieux utiliser
map
, quand vous en avez besoin pour obtenir des éléments en gamme, comme ils sont triés et vous pouvez simplement effectuer une itération sur eux à partir d'une frontière à l'autre.À partir de: http://www.cplusplus.com/reference/map/map/
"En interne, les éléments de la carte sont toujours triés par sa clé à la suite d'un spécifique faible stricte de la commande critère indiqué par la comparaison de l'objet (type de Comparer).
carte conteneurs sont généralement plus lents que unordered_map conteneurs pour accéder aux éléments individuels par leur clé, mais elles permettent de direct itération sur des sous-ensembles en fonction de leur ordre."