125 votes

map vs. hash_map en C++

J'ai une question avec hash_map y map en C++. Je comprends que map est en STL, mais hash_map n'est pas une norme. Quelle est la différence entre les deux ?

142voto

Joe Points 17829

Ils sont mis en œuvre de manière très différente.

hash_map ( unordered_map dans TR1 et Boost ; utilisez-les plutôt) utilisent une table de hachage où la clé est hachée dans un emplacement de la table et la valeur est stockée dans une liste liée à cette clé.

map est implémenté comme un arbre de recherche binaire équilibré (généralement un arbre rouge/noir).

Un site unordered_map devrait donner des performances légèrement meilleures pour l'accès aux éléments connus de la collection, mais un map aura des caractéristiques supplémentaires utiles (par exemple, il est stocké dans un ordre trié, ce qui permet de le parcourir du début à la fin). unordered_map sera plus rapide pour les insertions et les suppressions qu'une map .

7 votes

Je ne suis pas entièrement d'accord avec vous en ce qui concerne la performance. Les performances sont influencées par un certain nombre de paramètres et je réprimanderais n'importe quel programmeur utilisant un unordered_map pour seulement 10 entrées parce que "c'est plus rapide". Il faut d'abord se préoccuper de l'interface et des fonctionnalités, puis des performances.

26 votes

Eh bien, oui, ça aide si vous comprenez votre problème. Jusqu'à certains ordres de grandeur, les performances sont probablement nulles, mais il est important de comprendre les caractéristiques de performance des deux conteneurs, car elles varient de différentes manières lorsque les volumes de données augmentent.

7 votes

Il est intéressant de noter que je viens d'échanger une std::map avec une boost::unordered_map dans une application dans laquelle je fais beaucoup de recherches aléatoires, mais aussi des itérations sur toutes les clés de la carte. J'ai gagné une grande quantité de temps dans la recherche, mais je l'ai regagné par les itérations, donc je suis revenu à map et je cherche d'autres moyens d'améliorer les performances de l'application.

36voto

janglin Points 390

hash_map était une extension commune fournie par de nombreuses implémentations de bibliothèques. C'est exactement la raison pour laquelle elle a été renommée en unordered_map lorsqu'il a été ajouté à la norme C++ dans le cadre de TR1. map est généralement implémenté avec un arbre binaire équilibré comme un arbre rouge-noir (les implémentations varient bien sûr). hash_map y unordered_map sont généralement implémentés avec des tables de hachage. Ainsi, l'ordre n'est pas maintenu. unordered_map insert/delete/query sera O(1) (temps constant) où map sera O(log n) où n est le nombre d'éléments dans la structure de données. Donc unordered_map est plus rapide, et si vous ne vous souciez pas de l'ordre des éléments, il devrait être préféré à map . Parfois vous voulez maintenir l'ordre (ordonné par la clé) et pour cela map serait le choix.

9 votes

Je tiens à souligner que hashmap a un accès de O(N) dans le pire des cas lorsque des collisions sont probables (mauvais fcn de hachage, facteur de chargement trop élevé, etc).

0 votes

Un bon hashmap a un coût attendu de O(1), mais il n'est pas garanti qu'il en soit ainsi. Les mauvais hashmaps peuvent avoir un coût attendu qui n'est pas O(1).

16voto

R Samuel Klatchko Points 44549

Certaines des principales différences se situent au niveau des exigences de complexité.

  • A map nécessite O(log(N)) temps pour les opérations d'insertion et de recherche, car il est mis en œuvre en tant qu'outil de recherche. Arbre rouge-noir structure de données.

  • Un site unordered_map nécessite un temps "moyen" de O(1) pour les insertions et les recherches, mais il est autorisé à avoir un temps d'exécution le plus mauvais possible de O(N) . Cela est dû au fait qu'il est mis en œuvre en utilisant Table de hachage structure de données.

Donc, en général, unordered_map sera plus rapide, mais selon les clés et la fonction de hachage que vous stockez, cela peut devenir bien pire.

4voto

Warren Young Points 16324

La spécification C++ ne dit pas exactement quel algorithme vous devez utiliser pour les conteneurs STL. Elle impose cependant certaines contraintes sur leurs performances, ce qui exclut l'utilisation de tables de hachage pour les conteneurs STL. map et d'autres conteneurs associatifs. (Ils sont le plus souvent mis en œuvre avec des arbres rouge/noir.) Ces contraintes exigent de meilleures performances dans le pire des cas pour ces conteneurs que celles que les tables de hachage peuvent offrir.

Cependant, de nombreuses personnes veulent vraiment des tables de hachage, c'est pourquoi les conteneurs associatifs STL basés sur le hachage sont une extension courante depuis des années. Par conséquent, ils ont ajouté unordered_map et autres à des versions ultérieures de la norme C++.

0 votes

Elle a été ajoutée dans TR1 (std::tr1::unordered_map), pas dans C++0x.

0 votes

Je pensais que la raison map est généralement un btree équilibré était due à l'utilisation de operator<() comme moyen de déterminer l'emplacement.

0 votes

@kts : Est-ce que des implémentations de la STL utilisent réellement une arborescence B ?

4voto

Jayhello Points 703

map est mis en œuvre à partir de balanced binary search tree (généralement un rb_tree ), puisque tous les membres de balanced binary search tree est triée, la carte l'est aussi ;

hash_map est mis en œuvre à partir de hashtable Puisque tous les membres de hashtable n'est pas trié, donc les membres dans hash_map(unordered_map) n'est pas trié.

hash_map n'est pas une bibliothèque standard c++, mais elle est maintenant renommée en unordered_map (on peut penser qu'il a été renommé) et devient la bibliothèque standard c++ depuis c++11 ; voir cette question Différence entre hash_map et unordered_map ? pour plus de détails.

Ci-dessous, je vais donner une interface de base du code source de la façon dont la carte à deux types est mise en œuvre.

carte :

Le code ci-dessous est juste pour montrer que, la carte est juste un emballage d'un balanced binary search tree la quasi-totalité de sa fonction est d'invoquer la fonction balanced binary search tree fonction.

template <typename Key, typename Value, class Compare = std::less<Key>>
class map{
    // used for rb_tree to sort
    typedef Key    key_type;

    // rb_tree node value
    typedef std::pair<key_type, value_type> value_type;

    typedef Compare key_compare;

    // as to map, Key is used for sort, Value used for store value
    typedef rb_tree<key_type, value_type, key_compare> rep_type;

    // the only member value of map (it's  rb_tree)
    rep_type t;
};

// one construct function
template<typename InputIterator>
map(InputIterator first, InputIterator last):t(Compare()){
        // use rb_tree to insert value(just insert unique value)
        t.insert_unique(first, last);
}

// insert function, just use tb_tree insert_unique function
//and only insert unique value
//rb_tree insertion time is : log(n)+rebalance
// so map's  insertion time is also : log(n)+rebalance 
typedef typename rep_type::const_iterator iterator;
std::pair<iterator, bool> insert(const value_type& v){
    return t.insert_unique(v);
};

hash_map :

hash_map est mis en œuvre à partir de hashtable dont la structure est un peu comme ceci :

enter image description here

Dans le code ci-dessous, je vais donner la partie principale de hashtable et donne ensuite hash_map .

// used for node list
template<typename T>
struct __hashtable_node{
    T val;
    __hashtable_node* next;
};

template<typename Key, typename Value, typename HashFun>
class hashtable{
    public:
        typedef size_t   size_type;
        typedef HashFun  hasher;
        typedef Value    value_type;
        typedef Key      key_type;
    public:
        typedef __hashtable_node<value_type> node;

        // member data is buckets array(node* array)
        std::vector<node*> buckets;
        size_type num_elements;

        public:
            // insert only unique value
            std::pair<iterator, bool> insert_unique(const value_type& obj);

};

Comme map's Le seul membre est rb_tree le hash_map's Le seul membre est hashtable . Son code principal est le suivant :

template<typename Key, typename Value, class HashFun = std::hash<Key>>
class hash_map{
    private:
        typedef hashtable<Key, Value, HashFun> ht;

        // member data is hash_table
        ht rep;

    public:
        // 100 buckets by default
        // it may not be 100(in this just for simplify)
        hash_map():rep(100){};

        // like the above map's insert function just invoke rb_tree unique function
        // hash_map, insert function just invoke hashtable's unique insert function
        std::pair<iterator, bool> insert(const Value& v){
                return t.insert_unique(v);
        };

};

L'image ci-dessous montre la structure interne d'un hash_map lorsqu'il a 53 buckets, et qu'il insère des valeurs.

enter image description here

L'image ci-dessous montre quelques différences entre map et hash_map(unordered_map), l'image provient de Comment choisir entre map et unordered_map ? :

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X