STLにはHashMap APIがあることは知っていますが、これに関する優れた例のある優れた完全なドキュメントは見つかりません。
良い例はありがたいです。
標準ライブラリには、順序付きおよび順序なしマップ( std::map
および std::unordered_map
)コンテナーが含まれています。順序付けられたマップでは、要素はキー、挿入、およびアクセスでソートされます O(log n) 。通常、標準ライブラリは内部で red black trees を順序付きマップに使用します。しかし、これは実装の詳細にすぎません。順序付けられていないマップでは、挿入とアクセスはO(1)にあります。ハッシュテーブルの単なる別の名前です。
(順序付けされた)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 present
if (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';
return 0;
}
出力:
23 キー:hello値:23
コンテナで注文する必要があり、O(log n)ランタイムで問題ない場合は、std::map
を使用します。
それ以外の場合、本当にハッシュテーブル(O(1)の挿入/アクセス)が必要な場合は、std::unordered_map
APIに似たstd::map
を確認してください(たとえば、上の例では、map
を検索してunordered_map
に置き換えるだけです)。
unordered_map
コンテナは C++ 11標準 リビジョンで導入されました。したがって、コンパイラに応じて、C++ 11機能を有効にする必要があります(たとえば、GCC 4.8を使用する場合は、-std=c++11
をCXXFLAGSに追加する必要があります)。
C++ 11リリースの前でも、GCCはunordered_map
をサポートしていました-名前空間std::tr1
で。したがって、古いGCCコンパイラの場合、次のように使用することができます。
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
また、ブーストの一部です。つまり、移植性を高めるために対応する boost-header を使用できます。
hash_map
は、標準化のためにunordered_map
と呼ばれるものの古い、標準化されていないバージョンです(元々はTR1で、C++ 11以降の標準に含まれています)。名前が示すように、std::map
とは主に順序付けられていない点で異なります。たとえば、begin()
からend()
へのマップを反復処理すると、キー順にアイテムが取得されます。1、しかし、unordered_map
をbegin()
からend()
に繰り返し処理すると、アイテムは多かれ少なかれ任意の順序で取得されます。
unordered_map
は通常、一定の複雑さを持つことが期待されています。つまり、挿入、ルックアップなどには、通常、テーブル内のアイテムの数に関係なく、基本的に一定の時間がかかります。 std::map
の複雑さは、格納されているアイテムの数に対して対数的です。つまり、アイテムを挿入または取得する時間は長くなりますが、マップが大きくなると、かなりゆっくりになります。たとえば、100万アイテムの1つを検索するのに1マイクロ秒かかる場合、200万アイテムの1つを検索するのに約2マイクロ秒、400万アイテムの1つを3マイクロ秒、800万アイテムの1つを4マイクロ秒かかると予想できますアイテムなど.
実用的な観点から、それは実際には全体の話ではありません。本質的に、単純なハッシュテーブルのサイズは固定されています。汎用コンテナの可変サイズ要件にそれを適合させることは、いくぶん簡単ではありません。結果として、テーブルを(潜在的に)拡大する操作(挿入など)は、比較的遅い可能性があります(つまり、ほとんどはかなり高速ですが、定期的にはかなり遅くなります)。テーブルのサイズを変更できないルックアップは、一般的にはるかに高速です。結果として、ほとんどのハッシュベースのテーブルは、挿入数と比較して多くのルックアップを行うときに最高の状態になる傾向があります。大量のデータを挿入する場合、テーブルを1回繰り返して結果を取得します(たとえば、ファイル内の一意の単語の数をカウントする)可能性は、std::map
が同じくらい、そしておそらくさらに高速になることです(しかし繰り返しますが、計算の複雑さは異なるため、ファイル内の一意の単語の数にも依存します)。
1 マップを作成するときに、3番目のテンプレートパラメーターによって順序が定義される場所(デフォルトではstd::less<T>
)。
コンパイルエラーを生成するために必要なインクルードを省略しない、より完全で柔軟な例を次に示します。
#include <iostream>
#include <unordered_map>
class Hashtable {
std::unordered_map<const void *, const void *> htmap;
public:
void put(const void *key, const void *value) {
htmap[key] = value;
}
const void *get(const void *key) {
return htmap[key];
}
};
int main() {
Hashtable ht;
ht.put("Bob", "Dylan");
int one = 1;
ht.put("one", &one);
std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}
一致する値は機能しないため、ポインターとして事前に定義されていない限り、キーにはまだ特に有用ではありません! (ただし、通常はキーに文字列を使用するため、キーの宣言で「const void *」を「string」に置き換えることでこの問題を解決できます。)
std::unordered_map
がGCC stdlibc ++ 6.4でハッシュマップを使用するという証拠
https://stackoverflow.com/a/3578247/895245 で言及されましたが、次の回答では: C++のstd :: map内のデータ構造は? GCC stdlibc ++ 6.4の実装について、そのような証拠をさらに示しました。
その回答で説明されているパフォーマンス特性グラフのプレビューを次に示します。
unordered_map
でカスタムクラスとハッシュ関数を使用する方法
この答えはそれを釘付けにします: C++カスタムクラスタイプをキーとして使用するunordered_map
抜粋:平等:
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
ハッシュ関数:
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& 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);
}
};
}