map というデータ構造は連想配列にあたるものと単純に理解していた。unordered_map について追って知ることとなり、初めて map のキーが自動的にソートされることを知る。

#include <map>
#include <unordered_map>

int main()
{
  std::map<int, int> mymap1;
  std::unordered_map<int, int> mymap2;

  int numbers[5] = {3, 1, 4, 1, 5};
  for (int i = 0; i < 5; ++i)
  {
    mymap1[numbers[i]]++;
    mymap2[numbers[i]]++;
  }

  std::printf("map\n");
  for (auto x : mymap1)
  {
    std::printf("%d: %d\n", x.first, x.second);
  }

  std::printf("\n");

  std::printf("unordered_map\n");
  for (auto x : mymap2)
  {
    std::printf("%d: %d\n", x.first, x.second);
  }

  return 0;
}
# 実行結果
map
1: 2
3: 1
4: 1
5: 1

unordered_map
5: 1
4: 1
1: 2
3: 1

 両者について、要素へのアクセスの時間計算量は、要素数をNとして次のようになる。

  complexity
map O(logN)
unordered_map O(1) (まれにO(N))

 unordered_map のアクセス速度が悪化するのは、要素数が増えてコンテナサイズの拡張が必要となるケースにおいてである。これは要素数が多くなるにつれて線的に悪化する。とはいえ平均的には O(1) で実行されるため、用途に応じて配慮するほかない。

 いうまでもなく、任意のサブセットに対してイテレートを行うようなときには、ソートが保証される map がより効率的である。他方で、任意の要素に繰り返しアクセスする必要があるような場合には、定数時間で処理できる unordered_map に軍配があがる。