Skip to main content

Associative Containers: Ordered vs Unordered

intermediate30 min readLesson 94 of 204

Tree vs hash tradeoffs, try_emplace, the map[key] insertion trap, and traversal with structured bindings.

Associative containers trade position for lookup. Two families:

#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

std::map<std::string, int> m;          // balanced tree: sorted order, O(log n)
std::unordered_map<std::string, int> u; // hash table: no order, O(1) average
std::set<std::string> s;               // unique keys, sorted
std::unordered_set<std::string> us;    // unique keys, hashed

The decision: do you need order?

  • Iterate in sorted key order or need lower_bound โ†’ the ordered family.
  • Only single-key lookup, fastest possible โ†’ the unordered family.

Insertion is uniform, and try_emplace avoids a useless construction when the key already exists:

m[key] += 1;                  // inserts 0 first if absent (operator[])
m.try_emplace(key, 0).first->second += 1;  // no double lookup

One trap worth knowing early: map[key] inserts when the key is missing. On a const map& you must use .at() or .find() โ€” operator[] does not even compile, and that constraint protects you.

Structures bindings make traversal read cleanly:

for (const auto& [key, value] : m) {
    // sorted by key here
}

Now practice

Associative container problemsSorted word counts and a dedupe that keeps the last occurrence.2 challenges ยท ยท ~30 min