Você está perguntando sobre o C ++ 1x hash_map ou sobre o std :: map?
Philant
2
Eu quero algo como um java.util.HashMap em C ++ e a maneira padronizada de fazê-lo, se houver um. Caso contrário, a melhor biblioteca não padrão. O que os desenvolvedores de C ++ geralmente usam quando precisam de um HashMap?
User855 26/08/10
Respostas:
238
A biblioteca padrão inclui os contêineres de mapa ( std::mape std::unordered_map) ordenados e não ordenados . Em um mapa ordenado, os elementos são classificados pela chave, inserção e acesso em O (log n) . Geralmente a biblioteca padrão usa internamente árvores negras vermelhas para mapas ordenados. Mas este é apenas um detalhe de implementação. Em um mapa não ordenado, insira e o acesso está em O (1). É apenas outro nome para uma hashtable.
Um exemplo com (pedido) 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 presentif(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';return0;}
Resultado:
23
Chave: olá Valor: 23
Se você precisar fazer um pedido em seu contêiner e estiver bem com o tempo de execução O (log n), basta usar std::map.
Caso contrário, se você realmente precisa de um hash-table (O (1) insert / acesso), confira std::unordered_map, que tem um similar à std::mapAPI (por exemplo, no exemplo acima, você só tem que procurar e substituir mapcom unordered_map).
O unordered_mapcontêiner foi introduzido com a revisão padrão do C ++ 11 . Portanto, dependendo do seu compilador, você precisa habilitar os recursos do C ++ 11 (por exemplo, ao usar o GCC 4.8, você deve adicionar -std=c++11ao CXXFLAGS).
Mesmo antes do lançamento do C ++ 11, o GCC era compatível unordered_map- no espaço para nome std::tr1. Assim, para os compiladores antigos do GCC, você pode tentar usá-lo assim:
#include<tr1/unordered_map>
std::tr1::unordered_map<std::string,int> m;
Também faz parte do impulso, ou seja, você pode usar o cabeçalho de impulso correspondente para melhor portabilidade.
Embora a biblioteca padrão não tenha um contêiner baseado em tabela de hash, quase todas as implementações incluem hash_mapdo SGI STL de uma forma ou de outra.
James McNellis
@JamesMcNellis que é aconselhada unordered_map ou hash_map para implementação HashMap
Shameel Mohamed
2
@ ShameelMohamed, 2017, ou seja, 6 anos após o C ++ 11, deve ser difícil encontrar um STL que não forneça unordered_map. Portanto, não há razão para considerar o não-padrão hash_map.
maxschlepzig
30
A hash_mapé uma versão mais antiga e não padronizada do que, para fins de padronização, é chamado de unordered_map(originalmente em TR1 e incluído no padrão desde C ++ 11). Como o nome indica, é diferente de std::mapser desordenado - se, por exemplo, você percorre um mapa de begin()para end(), obtém os itens em ordem pela chave 1 , mas se percorre um unordered_mapde begin()para end(), obtém itens em um ordem mais ou menos arbitrária.
A unordered_map é normalmente esperado para ter complexidade constante. Ou seja, uma inserção, pesquisa etc. geralmente leva essencialmente um tempo fixo, independentemente de quantos itens estão na tabela. Um std::maptem uma complexidade logarítmica no número de itens que estão sendo armazenados - o que significa que o tempo para inserir ou recuperar um item aumenta, mas muito lentamente , à medida que o mapa aumenta. Por exemplo, se leva 1 microssegundo para pesquisar um dos 1 milhão de itens, você pode esperar cerca de 2 microssegundos para pesquisar um dos 2 milhões de itens, 3 microssegundos para um dos 4 milhões de itens, 4 microssegundos para um dos 8 milhões itens etc.
Do ponto de vista prático, essa não é a história toda. Por natureza, uma tabela de hash simples tem um tamanho fixo. A adaptação aos requisitos de tamanho variável para um contêiner de uso geral não é trivial. Como resultado, as operações que (potencialmente) aumentam a tabela (por exemplo, inserção) são potencialmente relativamente lentas (ou seja, a maioria é razoavelmente rápida, mas periodicamente uma será muito mais lenta). As pesquisas, que não podem alterar o tamanho da tabela, geralmente são muito mais rápidas. Como resultado, a maioria das tabelas baseadas em hash costuma ter o melhor desempenho quando você faz muitas pesquisas em comparação ao número de inserções. Para situações em que você insere muitos dados, repita a tabela uma vez para recuperar os resultados (por exemplo, contando o número de palavras exclusivas em um arquivo).std::map será tão rápido e possivelmente até mais rápido (mas, novamente, a complexidade computacional é diferente, de modo que também pode depender do número de palavras exclusivas no arquivo).
1 Onde a ordem é definida pelo terceiro parâmetro do modelo quando você cria o mapa, std::less<T>por padrão.
Sei que estou chegando 9 anos após a resposta ter sido publicada, mas ... você tem um link para um documento que mencione o fato de que um mapa não ordenado pode diminuir de tamanho? Normalmente, as coleções std apenas crescem. Além disso, se você inserir muitos dados, mas souber com antecedência mais ou menos quantas chaves você inserirá, poderá especificar o tamanho do mapa na criação, o que basicamente anula o custo de redimensionamento (porque não haverá nenhum) .
Zonko
@Zonko: Desculpe, eu não percebi isso quando solicitado. Tanto quanto eu sei, um unordered_map não diminui, exceto em resposta à chamada rehash. Ao ligar rehash, você especifica um tamanho para a tabela. Esse tamanho será usado, a menos que isso exceda o fator de carga máximo especificado para a tabela (nesse caso, o tamanho será aumentado automaticamente para manter o fator de carga dentro dos limites).
Jerry Coffin
22
Aqui está um exemplo mais completo e flexível que não omite as inclusões necessárias para gerar erros de compilação:
Ainda não é particularmente útil para chaves, a menos que sejam predefinidas como ponteiros, porque um valor correspondente não serve! (No entanto, como normalmente uso cadeias de caracteres para chaves, substituir "string" por "const void *" na declaração da chave deve resolver esse problema.)
Devo dizer que este exemplo é uma prática muito ruim em C ++. Você está usando um idioma fortemente tipado e o destrói usando void*. Para iniciantes, não há motivo para quebrar o unordered_mapcódigo, pois faz parte do padrão e reduz a manutenção do código. Em seguida, se insistir em envolvê-lo, use templates. É exatamente para isso que eles servem.
Guyarad 16/05/19
Fortemente digitado? Você provavelmente quer dizer digitado estaticamente. O fato de ele poder passar de const char ptr para void silenciosamente torna o C ++ estaticamente, mas não fortemente, digitado. Existem tipos, mas o compilador não diz nada a menos que você ative algum sinalizador obscuro que provavelmente não existe.
Sahsahae 04/12/19
6
Evidência que std::unordered_mapusa um mapa de hash no GCC stdlibc ++ 6.4
structKey{
std::string first;
std::string second;int third;booloperator==(constKey&other)const{return(first == other.first
&& second == other.second
&& third == other.third);}};
Função hash:
namespace std {template<>struct hash<Key>{
std::size_toperator()(constKey& 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);}};}
No espaço para nome padrão, declare uma estrutura de modelo chamada hash com o nome da classe como o tipo (veja abaixo). Encontrei um ótimo post no blog que também mostra um exemplo de cálculo de hashes usando XOR e deslocamento de bits, mas isso está fora do escopo desta pergunta, mas também inclui instruções detalhadas sobre como realizar o uso de funções hash também https://prateekvjoshi.com/ 06/06/2014 / using-hash-function-in-c-for-user-defined-classes /
namespace std {template<>struct hash<my_type>{size_toperator()(const my_type& k){// Do your hash function here...}};}
Portanto, para implementar uma hashtable usando sua nova função hash, você apenas precisa criar uma std::mapou std::unordered_mapexatamente como faria normalmente e usar my_typecomo chave, a biblioteca padrão usará automaticamente a função hash que você definiu anteriormente (na etapa 2) para hash suas chaves.
Respostas:
A biblioteca padrão inclui os contêineres de mapa (
std::map
estd::unordered_map
) ordenados e não ordenados . Em um mapa ordenado, os elementos são classificados pela chave, inserção e acesso em O (log n) . Geralmente a biblioteca padrão usa internamente árvores negras vermelhas para mapas ordenados. Mas este é apenas um detalhe de implementação. Em um mapa não ordenado, insira e o acesso está em O (1). É apenas outro nome para uma hashtable.Um exemplo com (pedido)
std::map
:Resultado:
Se você precisar fazer um pedido em seu contêiner e estiver bem com o tempo de execução O (log n), basta usar
std::map
.Caso contrário, se você realmente precisa de um hash-table (O (1) insert / acesso), confira
std::unordered_map
, que tem um similar àstd::map
API (por exemplo, no exemplo acima, você só tem que procurar e substituirmap
comunordered_map
).O
unordered_map
contêiner foi introduzido com a revisão padrão do C ++ 11 . Portanto, dependendo do seu compilador, você precisa habilitar os recursos do C ++ 11 (por exemplo, ao usar o GCC 4.8, você deve adicionar-std=c++11
ao CXXFLAGS).Mesmo antes do lançamento do C ++ 11, o GCC era compatível
unordered_map
- no espaço para nomestd::tr1
. Assim, para os compiladores antigos do GCC, você pode tentar usá-lo assim:Também faz parte do impulso, ou seja, você pode usar o cabeçalho de impulso correspondente para melhor portabilidade.
fonte
hash_map
do SGI STL de uma forma ou de outra.unordered_map
. Portanto, não há razão para considerar o não-padrãohash_map
.A
hash_map
é uma versão mais antiga e não padronizada do que, para fins de padronização, é chamado deunordered_map
(originalmente em TR1 e incluído no padrão desde C ++ 11). Como o nome indica, é diferente destd::map
ser desordenado - se, por exemplo, você percorre um mapa debegin()
paraend()
, obtém os itens em ordem pela chave 1 , mas se percorre umunordered_map
debegin()
paraend()
, obtém itens em um ordem mais ou menos arbitrária.A
unordered_map
é normalmente esperado para ter complexidade constante. Ou seja, uma inserção, pesquisa etc. geralmente leva essencialmente um tempo fixo, independentemente de quantos itens estão na tabela. Umstd::map
tem uma complexidade logarítmica no número de itens que estão sendo armazenados - o que significa que o tempo para inserir ou recuperar um item aumenta, mas muito lentamente , à medida que o mapa aumenta. Por exemplo, se leva 1 microssegundo para pesquisar um dos 1 milhão de itens, você pode esperar cerca de 2 microssegundos para pesquisar um dos 2 milhões de itens, 3 microssegundos para um dos 4 milhões de itens, 4 microssegundos para um dos 8 milhões itens etc.Do ponto de vista prático, essa não é a história toda. Por natureza, uma tabela de hash simples tem um tamanho fixo. A adaptação aos requisitos de tamanho variável para um contêiner de uso geral não é trivial. Como resultado, as operações que (potencialmente) aumentam a tabela (por exemplo, inserção) são potencialmente relativamente lentas (ou seja, a maioria é razoavelmente rápida, mas periodicamente uma será muito mais lenta). As pesquisas, que não podem alterar o tamanho da tabela, geralmente são muito mais rápidas. Como resultado, a maioria das tabelas baseadas em hash costuma ter o melhor desempenho quando você faz muitas pesquisas em comparação ao número de inserções. Para situações em que você insere muitos dados, repita a tabela uma vez para recuperar os resultados (por exemplo, contando o número de palavras exclusivas em um arquivo).
std::map
será tão rápido e possivelmente até mais rápido (mas, novamente, a complexidade computacional é diferente, de modo que também pode depender do número de palavras exclusivas no arquivo).1 Onde a ordem é definida pelo terceiro parâmetro do modelo quando você cria o mapa,
std::less<T>
por padrão.fonte
rehash
. Ao ligarrehash
, você especifica um tamanho para a tabela. Esse tamanho será usado, a menos que isso exceda o fator de carga máximo especificado para a tabela (nesse caso, o tamanho será aumentado automaticamente para manter o fator de carga dentro dos limites).Aqui está um exemplo mais completo e flexível que não omite as inclusões necessárias para gerar erros de compilação:
Ainda não é particularmente útil para chaves, a menos que sejam predefinidas como ponteiros, porque um valor correspondente não serve! (No entanto, como normalmente uso cadeias de caracteres para chaves, substituir "string" por "const void *" na declaração da chave deve resolver esse problema.)
fonte
void*
. Para iniciantes, não há motivo para quebrar ounordered_map
código, pois faz parte do padrão e reduz a manutenção do código. Em seguida, se insistir em envolvê-lo, usetemplates
. É exatamente para isso que eles servem.Evidência que
std::unordered_map
usa um mapa de hash no GCC stdlibc ++ 6.4Isso foi mencionado em: https://stackoverflow.com/a/3578247/895245, mas na resposta a seguir: Que estrutura de dados está dentro de std :: map em C ++? Forneci evidências adicionais para a implementação do GCC stdlibc ++ 6.4 por:
Aqui está uma prévia do gráfico de características de desempenho descrito nessa resposta:
Como usar uma função de classe e hash personalizada com
unordered_map
Esta resposta explica: C ++ unordered_map usando um tipo de classe personalizado como a chave
Trecho: igualdade:
Função hash:
fonte
Para aqueles que tentam descobrir como fazer o hash de nossas próprias classes enquanto ainda usam o modelo padrão, existe uma solução simples:
Na sua classe, você precisa definir uma sobrecarga de operador de igualdade
==
. Se você não sabe como fazer isso, o GeeksforGeeks possui um ótimo tutorial https://www.geeksforgeeks.org/operator-overloading-c/No espaço para nome padrão, declare uma estrutura de modelo chamada hash com o nome da classe como o tipo (veja abaixo). Encontrei um ótimo post no blog que também mostra um exemplo de cálculo de hashes usando XOR e deslocamento de bits, mas isso está fora do escopo desta pergunta, mas também inclui instruções detalhadas sobre como realizar o uso de funções hash também https://prateekvjoshi.com/ 06/06/2014 / using-hash-function-in-c-for-user-defined-classes /
std::map
oustd::unordered_map
exatamente como faria normalmente e usarmy_type
como chave, a biblioteca padrão usará automaticamente a função hash que você definiu anteriormente (na etapa 2) para hash suas chaves.fonte