Tenho uma pergunta com hash_map
e map
em C ++. Eu entendo que map
está em STL, mas hash_map
não é um padrão. Qual a diferença entre os dois?
Tenho uma pergunta com hash_map
e map
em C ++. Eu entendo que map
está em STL, mas hash_map
não é um padrão. Qual a diferença entre os dois?
Respostas:
Eles são implementados de maneiras muito diferentes.
hash_map
( unordered_map
em TR1 e Boost; use-os em vez disso) use uma tabela hash onde a chave é hash para um slot na tabela e o valor é armazenado em uma lista vinculada a essa chave.
map
é implementado como uma árvore de pesquisa binária balanceada (geralmente uma árvore vermelha / preta).
Um unordered_map
deve ter um desempenho ligeiramente melhor para acessar elementos conhecidos da coleção, mas um map
terá características adicionais úteis (por exemplo, ele é armazenado em ordem classificada, o que permite a travessia do início ao fim). unordered_map
será mais rápido na inserção e exclusão do que a map
.
hash_map
foi uma extensão comum fornecida por muitas implementações de biblioteca. É exatamente por isso que foi renomeado para unordered_map
quando foi adicionado ao padrão C ++ como parte do TR1. map é geralmente implementado com uma árvore binária balanceada como uma árvore vermelha e preta (as implementações variam, é claro). hash_map
e unordered_map
geralmente são implementados com tabelas de hash. Portanto, a ordem não é mantida. unordered_map
inserir / excluir / consultar será O (1) (tempo constante), onde map será O (log n), onde n é o número de itens na estrutura de dados. Então unordered_map
é mais rápido, e se você não se preocupa com a ordem dos itens deve dar preferência map
. Às vezes você quer manter a ordem (ordenada pela chave) e para isso map
seria a escolha.
Algumas das principais diferenças estão nos requisitos de complexidade.
A map
requer O(log(N))
tempo para inserir e localizar operações, pois é implementado como uma estrutura de dados Red-Black Tree .
Um unordered_map
requer um tempo 'médio' de O(1)
para inserções e descobertas, mas pode ter um tempo de pior caso de O(N)
. Isso ocorre porque é implementado usando a estrutura de dados Hash Table .
Então, normalmente, unordered_map
será mais rápido, mas dependendo das chaves e da função hash que você armazena, pode ficar muito pior.
A especificação C ++ não diz exatamente qual algoritmo você deve usar para os contêineres STL. No entanto, ele impõe certas restrições ao desempenho deles, o que exclui o uso de tabelas hash para map
e outros contêineres associativos. (Eles são mais comumente implementados com árvores vermelhas / pretas.) Essas restrições requerem melhor desempenho de pior caso para esses contêineres do que as tabelas de hash podem fornecer.
Muitas pessoas realmente querem tabelas de hash, portanto, os contêineres associativos STL baseados em hash têm sido uma extensão comum por anos. Consequentemente, eles adicionaram unordered_map
e tal a versões posteriores do padrão C ++.
map
é geralmente uma árvore balanceada devido ao uso operator<()
como meio de determinar a localização.
map
é implementado a partir de balanced binary search tree
(geralmente a rb_tree
), uma vez que todo o membro em balanced binary search tree
é classificado, assim como o mapa;
hash_map
é implementado a partir de. hashtable
Visto que todo o membro em hashtable
não está classificado, os membros em hash_map(unordered_map)
não são classificados.
hash_map
não é uma biblioteca padrão c ++, mas agora é renomeada para unordered_map
(você pode pensar nela renomeada) e se torna a biblioteca padrão c ++ desde c ++ 11 veja esta questão Diferença entre hash_map e unordered_map? para mais detalhes.
Abaixo, apresentarei algumas interfaces básicas do código-fonte de como o mapa de dois tipos é implementado.
O código abaixo é apenas para mostrar que, map é apenas um envoltório de um balanced binary search tree
, quase toda a sua função é apenas invocar a balanced binary search tree
função.
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
é implementado de hashtable
cuja estrutura é mais ou menos assim:
No código abaixo, darei a parte principal do hashtable
e, em seguida,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);
};
Como map's
apenas um membro érb_tree
, o hash_map's
único membro é hashtable
. É o código principal abaixo:
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);
};
};
A imagem abaixo mostra quando um hash_map tem 53 buckets, e insere alguns valores, sua estrutura interna.
A imagem abaixo mostra algumas diferenças entre map e hash_map (unordered_map), a imagem vem de Como escolher entre map e unordered_map? :
Eu não sei o que dá, mas, hash_map leva mais de 20 segundos para limpar () 150K chaves inteiras não assinadas e valores flutuantes. Estou apenas executando e lendo o código de outra pessoa.
É assim que inclui hash_map.
#include "StdAfx.h"
#include <hash_map>
Eu li isso aqui https://bytes.com/topic/c/answers/570079-perfomance-clear-vs-swap
dizendo que clear () é a ordem de O (N). Isso pra mim é muito estranho, mas é assim que é.