Tenho uma pergunta com hash_mape mapem C ++. Eu entendo que mapestá em STL, mas hash_mapnão é um padrão. Qual a diferença entre os dois?
Tenho uma pergunta com hash_mape mapem C ++. Eu entendo que mapestá em STL, mas hash_mapnão é um padrão. Qual a diferença entre os dois?
Respostas:
Eles são implementados de maneiras muito diferentes.
hash_map( unordered_mapem 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_mapdeve ter um desempenho ligeiramente melhor para acessar elementos conhecidos da coleção, mas um mapterá características adicionais úteis (por exemplo, ele é armazenado em ordem classificada, o que permite a travessia do início ao fim). unordered_mapserá mais rápido na inserção e exclusão do que a map.
hash_mapfoi uma extensão comum fornecida por muitas implementações de biblioteca. É exatamente por isso que foi renomeado para unordered_mapquando 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_mape unordered_mapgeralmente são implementados com tabelas de hash. Portanto, a ordem não é mantida. unordered_mapinserir / 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 mapseria a escolha.
Algumas das principais diferenças estão nos requisitos de complexidade.
A maprequer O(log(N))tempo para inserir e localizar operações, pois é implementado como uma estrutura de dados Red-Black Tree .
Um unordered_maprequer 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_mapserá 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 mape 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_mape 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. hashtableVisto que todo o membro em hashtablenão está classificado, os membros em hash_map(unordered_map)não são classificados.
hash_mapnã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 treefunçã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'sapenas 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 é.