C ++でHashMapを使用する最良の方法は何ですか?


174

STLにはHashMap APIが含まれていることは知っていますが、これに関する優れた例が記載された適切で完全なドキュメントは見つかりません。

良い例はありがたいです。


C ++ 1x hash_mapについて、またはstd :: mapについて質問していますか?
フィランティング、

2
C ++のjava.util.HashMapのようなものと、それがある場合にそれを行うための標準化された方法が必要です。そうでなければ最高の非標準ライブラリ。C ++開発者がHashMapを必要とする場合、通常何を使用しますか?
user855 2010

回答:


237

標準ライブラリには、順序付きおよび順序なしのマップ(std::mapおよびstd::unordered_map)コンテナが含まれています。順序付けられたマップでは、要素はキーによってソートされ、挿入およびアクセスはO(log n)にあります。通常、標準ライブラリは、順序付けされたマップに内部で赤黒木を使用します。しかし、これは単なる実装の詳細です。順不同のマップでは、挿入とアクセスはO(1)で行われます。これはハッシュテーブルの単なる別名です。

(ordered)の例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に似ていた、std::map(例えば上記の例では、あなただけの検索および置換する必要がAPIを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;

これはブーストの一部でもあります。つまり、対応するブーストヘッダーを使用して、移植性を向上させることができます。


1
一方で、標準ライブラリは、ハッシュテーブルベースのコンテナを持っていない、ほとんどすべての実装は、いくつかのフォームまたは別でSGIのSTLから。hash_map
James McNellis、2010

HashMap実装のためにunordered_mapまたはhash_mapが推奨される
@JamesMcNellis

2
@ ShameelMohamed、2017年、つまりC ++ 11の6年後、提供しないSTLを見つけるのは難しいはずunordered_mapです。したがって、非標準を考慮する理由はありませんhash_map
maxschlepzig 2017

30

A hash_mapは、標準化の目的で呼ばれているものの古い非標準化バージョンですunordered_map(元はTR1で、C ++ 11以降の標準に含まれています)。名前が示すように、std::map主に順序付けされていないこととは異なります。たとえば、マップfromからbegin()to を反復すると、end()キー1の順に項目が取得されますが、unordered_mapfrom begin()to を反復するend()と、多かれ少なかれ任意の順序。

unordered_map通常、一定の複雑さを持つことが期待されています。つまり、挿入、ルックアップなどは、テーブル内のアイテム数に関係なく、通常、基本的に一定の時間がかかります。Anがstd::map格納されているアイテムの数に対数だ複雑さを持っている-非常にアイテムを挿入したり、検索する時間が増えることはなく、そのゆっくりとマップが大きくなるにつれて、。たとえば、100万個のアイテムの1つを検索するのに1マイクロ秒かかる場合、200万個のアイテムの1つを検索するのに約2マイクロ秒かかると予想できます。アイテムなど

実用的な観点からは、それだけではありません。本質的に、単純なハッシュテーブルのサイズは固定されています。汎用コンテナーの可変サイズ要件にそれを適合させることは、いくらか重要です。その結果、テーブルを(潜在的に)拡張する操作(挿入など)は、比較的低速になる可能性があります(つまり、ほとんどがかなり高速ですが、定期的に遅くなります)。テーブルのサイズを変更できないルックアップは、一般的にはるかに高速です。その結果、ほとんどのハッシュベースのテーブルは、挿入数と比較して多くのルックアップを行うときに最高の状態になる傾向があります。大量のデータを挿入する場合は、テーブルを1回繰り返して結果を取得します(たとえば、ファイル内の一意の単語の数を数える)。std::map 同じくらい高速で、おそらくさらに高速になります(ただし、計算の複雑さが異なるため、ファイル内の一意の単語の数にも依存します)。


1std::less<T>デフォルトでは、マップの作成時に3番目のテンプレートパラメーターによって順序が定義されます。


1
回答が投稿されてから9年後に来ていることに気づきましたが...順序付けられていないマップのサイズが縮小する可能性があることを説明するドキュメントへのリンクはありますか?通常、stdコレクションは大きくなるだけです。さらに、多くのデータを挿入するが、挿入するキーの数が事前にわかっている場合は、作成時にマップのサイズを指定できます。これにより、サイズ変更のコストが基本的に無効になります(何もないため)。 。
Zonko、

@ゾンコ:申し訳ありませんが、尋ねられても気づきませんでした。私の知る限り、unordered_mapは、の呼び出しへの応答を除いて縮小しませんrehash。を呼び出すときrehashに、テーブルのサイズを指定します。そのサイズは、それがテーブルに指定された最大負荷係数を超えない限り使用されます(この場合、サイズは、負荷係数を制限内に保つために自動的に増加します)。
Jerry Coffin

22

コンパイルエラーを生成するために必要なインクルードを省略しない、より完全で柔軟な例を次に示します。

#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」に置き換えると、この問題が解決します。


4
私は言わなければならない、この例はC ++では非常に悪い習慣です。あなたは強く型付けされた言語を使用していて、それを使ってそれを破壊していvoid*ます。まずunordered_map、標準の一部であり、コードの保守性が低下するため、をラップする理由はありません。次に、ラッピングを主張する場合は、を使用しますtemplates。それがまさに彼らの目的です。
Guyarad

強く型付け?あなたはおそらく静的に型付けされたことを意味します。彼がconst char ptrからvoidに静かに移行できるという事実は、C ++を静的に型付けしますが、強く型付けすることはしません。タイプはありますが、存在しない可能性が高いあいまいなフラグを有効にしない限り、コンパイラーは何もしません。
Sahsahae、

6

std::unordered_mapGCC stdlibc ++ 6.4でハッシュマップを使用する証拠

これはhttps://stackoverflow.com/a/3578247/895245で言及されていましたが、次の回答で:C ++のstd :: map内にはどのようなデータ構造がありますか?私はGCC stdlibc ++ 6.4実装のそのような証拠を次のように提供しました。

  • クラスへのGDBステップのデバッグ
  • 性能特性分析

これは、その回答で説明されているパフォーマンス特性グラフのプレビューです。

ここに画像の説明を入力してください

カスタムクラスとハッシュ関数を使用する方法 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);
    }
  };

}

0

標準テンプレートを使用しながら自分のクラスをハッシュする方法を理解しようとしている私たちのために、簡単な解決策があります:

  1. クラスでは、等価演算子オーバーロードを定義する必要があります==。これを行う方法がわからない場合は、GeeksforGeeksにすばらしいチュートリアルがあります。https: //www.geeksforgeeks.org/operator-overloading-c/

  2. 標準の名前空間で、型としてクラス名を使用して、hashというテンプレート構造体を宣言します(以下を参照)。XORとビットシフトを使用してハッシュを計算する例も示す優れたブログ投稿を見つけましたが、これはこの質問の範囲外ですが、ハッシュ関数を使用して実行する方法の詳細な手順も含まれていますhttps://prateekvjoshi.com/ 2014/06/05 /ユーザー定義クラス用のcでのハッシュ関数の使用/

namespace std {

  template<>
  struct hash<my_type> {
    size_t operator()(const my_type& k) {
      // Do your hash function here
      ...
    }
  };

}
  1. したがって、新しいハッシュ関数を使用してハッシュテーブルを実装するには、を作成するstd::mapか、std::unordered_map通常のようにmy_typeキーとして使用するだけで、標準ライブラリは前に(手順2で)定義したハッシュ関数を自動的に使用してハッシュしますあなたの鍵。
#include <unordered_map>

int main() {
  std::unordered_map<my_type, other_type> my_map;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.