C++マニアック

STL の map(マップ)の使い方,how to use STL map,標準テンプレートライブラリ,standard template library,コンテナ,container

このページでは STL の map(マップ) の使い方について解説します。

  1. map(マップ)とは
  2. map の構造を理解する
  3. 要素が pair<const キーの型, 値の型> 型ということは
  4. 要素がキーでソートされて保存されるということは
  5. 要素が有るか無いかを判断するには
  6. STL 関連本

map とは ページの先頭へ

map とは STL のコンテナで、「任意の型のキー」から「値」を引く辞書を実現するもので、典型的には、文字列から文字列を引くような辞書を実現するのに使用されるので連想配列とも呼ばれます。

map を使用するには、次のように、ヘッダ map を #include しておく必要があります。

    #include <map>
    using namespace std;

そして、例えばキーとして string 型の文字列を、値として int 型を使用するような map を使うと、次のよう記述が可能になります。

    map<string, int> mapstriHeight;            // 山の名前から高さを得る連想配列を用意する。
    mapstriHeight["Fuji"] = 3776;                    // 富士山の高さを登録する。
    int iHeightOfFuji = mapstriHeight["Fuji"];       // 富士山の高さを読み出す。

要するに辞書の要素に添字演算子 [] を使ってアクセスできるということになるわけです。

map の構造を理解する ページの先頭へ

map を使うには、その中味の構造をある程度知っておく必要があります。と言っても、簡単なことですが。

まず map は、「キー」と「値の」ペア型である pair<const キーの型, 値の型> 型の要素の集まりで出来ています。例えば、上の例で言えば pair<const string, int> 型の要素の集まりで出来ています。

そして、上記ペアはキーでソートされて map の中に保存されます。(実際には二分木というデータ構造に保存されるのですが、これは map を使う上で重要というわけではありません。)

これら二点を理解しておけば map は、簡単に使えます。

要素が pair<const キーの型, 値の型> 型ということは ページの先頭へ

map の要素が pair<Key, Val> であるということは、map を巡回するときに(=トラバースするとも言いますが)、イテレータが指すものの型が pair<Key, Val> であるということです。つまり、次のように、for 文で map の要素を巡回するとき、キーと値のどちらもイテレータから知ることができるわけです。

    map<string, int> mapstriTable;  // map のデータ構造を用意する。
    // ここで、上記にデータを複数登録する。
    {for (map<string, int>::iterator itpairstri = mapstriTable.begin(); itpairstri != mapstriTable.end(); itpairstri++) {
        // イテレータは pair<const string, int> 型なので、
        string strKey = itpairstri->first ;     // イテレータからキーが得られる。
        int    iVal   = itpairstri->second;     // イテレータからが得られる。
    }}

そして、もうひとつ言えるのは、上記のように for 文で巡回するときには、要素がキーでソートされて昇順に得られるということです。

要素がキーでソートされて保存されるということは ページの先頭へ

「要素がキーでソートされて保存される」ということは、逆に言えば、キーはソート可能でなくてはならないということでもあります。ソートが可能でなくてはならないとは、キーは大小関係を判断できる型でなくてはならないということです。この点が map を使う上で唯一少し難易度が高い部分でしょう。

例えば int 型などの組み込み型は演算子 < が使用可能で、例えば int iA; が int iB; よりも小さいかどうかは式 iA < iB で計算できます。このように < 演算子が使える型は map のキーとして使うことが出来ます。

問題となるのは、自作のクラスオブジェクトを map のキーにしようとする場合で、当該クラスに < 演算子が定義されていないと、コンパイル時にその旨を示すエラーとなって、コンパイルが失敗します。

#pragma warning(disable: 4786) 
#include <map>
using namespace std;

class CManiac {};   // 自作のクラス  < 演算子の定義なし

int main() {
    CManiac maniac;
    map<CManiac, int> mapmaniaci; // ここまでは問題ないが、
    mapmaniaci[maniac] = 0;             // ここでコンパイルは失敗する。
    return 0;
}

そこで、自作のクラスオブジェクトを map のキーにしようとするときには、次のように < 演算子を当該クラスに実装するようにします。

class CManiac {
public:
    bool operator<(const CManiac& rmaniac) const {
        // *this が rmaniac よりも小さいという条件をコーディングし、bool 値を返す。
        return b;
    }
}

演算子(=オペレータ)の定義の仕方については オペレータのオーバーロード を参照してください。
特に < 演算子を定義する上での注意点については sort も参照してください。
STL のコンテナに入れるクラスを定義する上でのその他の注意点については STL の vector の解説を参照してください。

要素が有るか無いかを判断するには ページの先頭へ

上記までで、map を使う上で難しそうなことはほぼ無くなったと思われるのですが、map を [] 演算子でアクセスすると、当該要素が無かった場合、必ず新しい要素ができてしまうので、これを避けたい場合について述べておきます。

要するに [] 演算子を使う前に、キーに対応する要素が有るか無いかを確認するということですが、次のようにします。

    map<string, int> mapstriTable;
    // 何らかの初期化
    if (mapstriTable.find("Key") == mapstriTable.end()) {
        // キーが "Key" の要素がない場合の処理
    } else {
        // キーが "Key" の要素がある場合の処理
    }

要するに find() と end() を使って、要素の存在判定を行うわけです。

ナオ : 最後の項は蛇足っぽいわね。キーが "Key" って、初めは何のことか分かりにくいし、"Key" より "XYZ" とでもしたほうが良いんじゃないですか、店長?
店主 : まぁ、そうかもね。じゃ、直しといて...
ナオ : ??? 絶句。