mapコンテナ(mとする)でキーの有無を調べる場合、今まではメンバ関数のm.find()を呼ぶ方法を使っていた。m.find()を使う方法では、「m.find()の戻り値がm.end()に等しければキーが存在しない、そうでなければキーが存在する」としてキーの有無を判別していた。
しかし、m.count()を使った方がより直感的である。m.count()を使う方法では、「m.count()の戻り値が0に等しければキーが存在しない、1に等しければキーが存在する」という簡単仕様なので分かりやすい。
以下のコードでは、find()を使う場合とcount()を使う場合とを比較した。どちらも結果は同じである。
#include <iostream> #include <map> using namespace std; bool has_key_using_find(map<int, string> &m, int n){ if (m.find(n) == m.end()){ cout << "m doesn't have " << n << "." << endl; return false; } else{ cout << "m has " << n << "." << endl; return true; } } bool has_key_using_count(map<int, string> &m, int n){ if (m.count(n) == 0){ cout << "m doesn't have " << n << "." << endl; return false; } else{ cout << "m has " << n << "." << endl; return true; } } int main(void){ map<int, string> m; m[1] = "a"; m[3] = "bb"; m[5] = "ccc"; has_key_using_find(m, 3); // true has_key_using_find(m, 4); // false has_key_using_count(m, 3); // true has_key_using_count(m, 4); // false return 0; }
出力はこんな感じ。
m has 3. m doesn't have 4. m has 3. m doesn't have 4.
(追記1: 2018-05-14) 意外とこの記事の参照数が多いので注意喚起。multimapコンテナやmultisetコンテナを使う場合は、キーの有無を調べるのにcount()を使うのは基本的に避けるべき。multimapやmultisetは、mapと異なり、同じキーを何度も登録することができる。count()を使うとキーの登録数を調べるために木の走査が入ってしまうので、単にキーの有無を調べる用途にはオーバーキルである。
以下に、同じキーを20000回登録したmultimapに対して、キーの有無を先述の2種類の方法で問い合わせしたときの処理時間を測るコードを掲載する。
#include <chrono> #include <iostream> #include <map> using namespace std; typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; #define REP(i,n) for(int i = 0; i < (int)(n); ++i) #define FOR(i,a,b) for(int i = (a); i < (int)(b); ++i) #define ALL(c) (c).begin(), (c).end() #define SIZE(v) ((int)v.size()) #define pb push_back #define mp make_pair #define mt make_tuple bool has_key_using_find(multimap<int, string> &m, int n){ if (m.find(n) == m.end()){ return false; } else{ return true; } } bool has_key_using_count(multimap<int, string> &m, int n){ if (m.count(n) == 0){ return false; } else{ return true; } } int main(void) { multimap<int, string> mm; for(int i = 0; i < 100000; ++i) { mm.insert(make_pair(i % 5, "value")); // 0 ~ 4までの5種のキーがそれぞれ20000個ずつ登録される } int TRY_NUM = 10000; auto start1 = chrono::system_clock::now(); for(int i = 0; i < TRY_NUM; ++i) { has_key_using_find(mm, 4); // true has_key_using_find(mm, 5); // false } auto end1 = chrono::system_clock::now(); auto diff1 = end1 - start1; auto start2 = chrono::system_clock::now(); for(int i = 0; i < TRY_NUM; ++i) { has_key_using_count(mm, 4); // true has_key_using_count(mm, 5); // false } auto end2 = chrono::system_clock::now(); auto diff2 = end2 - start2; cout << "find: " << chrono::duration_cast<chrono::milliseconds>(diff1).count() << "msec" << endl; cout << "count: " << chrono::duration_cast<chrono::milliseconds>(diff2).count() << "msec" << endl; return 0; }
CygwinのG++でビルドして実行した結果は以下のとおり。500倍ほど処理時間に差がついた。
find: 9msec count: 4982msec
(追記2: 2018-05-14)
c++ - Checking for existence in std::map - count vs find - Stack Overflow にて、この記事で書いたのと同じ議論が展開されている。そのうちの一つの回答(c++ - Checking for existence in std::map - count vs find - Stack Overflow) では、mapの処理系のソースコードを読んで、findの方がcountよりよいと結論づけている。ただ、このコードを見ると、GCCの方はfindもcountもほぼ同等のコードなのできっと速度は誤差程度にしか変わらない。一方VSの方はコードが複雑で、コードを追う根性がない…。
私が個人でC++コードを書くときはきっとcountを使うだろうけど、もしかするとチームでコーディングする場合ではfindを使う方がよいのかもしれない。理由の一つは、findを使うのはよく知られたイディオムであること。もう一つは、mapからmultimapにコンテナを変更したときにcountを修正し忘れ、速度でペナルティを受ける可能性があること。
参考: