Last 30 Days
No notifications
The STL gives you battle-tested containers for almost any data structure you'd hand-roll. Pick the right one and your code is fast and correct.
| Container | Header | Best for |
vector | | Default. Dynamic array. |
array | | Fixed-size, stack-allocated. |
map | | Sorted keyβvalue, O(log n). |
unordered_map | | Hash table, O(1) avg. |
set / unordered_set | / | Unique elements. |
#include <map>
#include <unordered_map>
#include <set>map<string, int> ages; // sorted by key
unordered_map<string, int> hashed; // hashed
set<int> uniques; // sorted unique ints
ages["Asha"] = 19;
ages["Ravi"] = 21;
ages.contains("Asha"); // C++20: true
auto it = ages.find("Ravi");
if (it != ages.end()) cout << it->first << "β" << it->second;for (const auto& [name, age] : ages) cout << name << ":" << age << "\n";
| Container | Use case |
deque | Push/pop both ends in O(1) |
list | Doubly linked list |
stack | LIFO β adapter on deque/vector |
queue | FIFO β adapter |
priority_queue | Max-heap by default |
Containers fall into three families:
| Family | Examples | Notes |
| Sequence | vector, deque, list, array | Order is what you put in |
| Associative | map, set, multimap, multiset | Keys sorted; tree-based; O(log n) |
| Unordered associative | unordered_map, unordered_set, ... | Hash table; O(1) average |
| Adapters | stack, queue, priority_queue | Restricted-interface wrappers |
std::vector (recap)Dynamic array β your default. Covered in detail in the vector topic. Reach for vector first; switch only when measurements demand.
std::arrayA thin wrapper over a fixed-size C array. Stack-allocated, size known at compile time, but you get size(), begin(), end(), range-for, comparison, etc.
#include <array>
array<int, 4> a{1, 2, 3, 4};
for (int x : a) cout << x;Use when you know the size at compile time and don't want heap allocation.
std::map β Sorted KeyβValueImplemented as a balanced binary tree (red-black tree). Operations are O(log n) and iteration is sorted by key.
map<string, int> scores;
scores["alice"] = 90; // insert/assign
scores["bob"] = 75;
scores.insert({"carol", 88});
scores.emplace("dave", 60); // construct in placefor (const auto& [name, score] : scores) cout << name << ":" << score << "\n";
// Iterates ALPHABETICALLY by key
if (auto it = scores.find("alice"); it != scores.end()) {
cout << it->second;
}
// C++20:
if (scores.contains("alice")) { /* ... */ }
scores.erase("bob");
> β οΈ m[key] inserts a default-constructed value if the key is missing. Use m.find() or m.contains() to check without inserting.
std::unordered_map β Hash TableSame interface as map, but:
unordered_map<string, int> counts;
counts["hello"]++;
counts["world"]++;For built-in types and string, the standard hash works out of the box. Custom types need a hasher.
> Default to unordered_map unless you need sorted iteration or you've measured a problem.
std::set / std::unordered_setLike maps but only keys β sorted unique values, or hashed unique values:
set<int> ids{3, 1, 4, 1, 5}; // {1, 3, 4, 5} β duplicates collapsed
ids.insert(9);
if (ids.count(3)) cout << "yes"; // β count(0|1) β works on all setsunordered_set<string> seen;
seen.insert("alpha");
std::dequeDouble-ended queue. push_front and push_back both O(1). Indexing is O(1) too. Memory layout is segmented, so worse cache behaviour than vector.
deque<int> d;
d.push_back(1);
d.push_front(0); // [0, 1]std::list β Doubly Linked ListO(1) insert/erase anywhere if you have an iterator. Random access is O(n) β no [] operator. Cache-unfriendly.
> Rarely the right answer. Use vector or deque unless you measurably need O(1) splice.
stack, queue, priority_queueThese wrap an underlying container with a restricted interface.
stack β LIFO#include <stack>
stack<int> s;
s.push(1); s.push(2); s.push(3);
cout << s.top(); // 3
s.pop();queue β FIFO#include <queue>
queue<int> q;
q.push(1); q.push(2);
cout << q.front(); // 1
q.pop();priority_queue β Max-Heappriority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
cout << pq.top(); // 4 β largest
pq.pop();// Min-heap:
priority_queue<int, vector<int>, greater<int>> minpq;
Used heavily in graph algorithms (Dijkstra, A*, k-largest, etc.).
Need order matters?
ββ Yes β vector (default), array (fixed size), deque (push both ends), list (O(1) splice)
ββ No β
Lookup by key?
ββ Yes β unordered_map (default), map (need sorted)
ββ No β unordered_set (default), set (need sorted)| Operation | vector | map / set | unordered_map / unordered_set |
| Insert | push_back O(1) amortised | insert / emplace O(log n) | O(1) avg |
| Find | std::find O(n) | .find() O(log n) | O(1) avg |
| Erase | by iterator | by key or iterator | by key or iterator |
| Iterate | order-of-insertion | sorted by key | unspecified order |
| Random access | v[i] O(1) | none | none |
map<string, int> m{{"a", 1}, {"b", 2}};// pre-C++17
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->first << ":" << it->second << "\n";
// C++17 structured bindings β much cleaner
for (const auto& [key, value] : m)
cout << key << ":" << value << "\n";
m["a"] = 5; // overwrites if "a" exists
m.insert({"a", 5}); // does NOT overwrite if "a" exists
m.try_emplace("a", 5); // C++17 β most efficient| Bug | Fix | |||
m["key"] inserts default value | Use m.find() or m.contains() first | |||
Iterator invalidation in vector after push_back | reserve, or refetch iterators | |||
| Hashing custom types | Provide std::hash specialisation | |||
Using map when you don't need sort | Switch to unordered_map for speed | Cheat-Sheet | Need | Container |
| Default | vector | |||
| Fixed size | array | |||
| KeyβValue, fast | unordered_map | |||
| KeyβValue, sorted | map | |||
| Unique values, fast | unordered_set | |||
| Unique values, sorted | set | |||
| Push both ends | deque | |||
| LIFO | stack | |||
| FIFO | queue | |||
| Largest first | priority_queue |
You now know which container to grab for any problem. Phase 4 starts with STL algorithms β the verbs that operate on these containers.