std::unordered_map
std::unordered_map
is an associative container in the C++ Standard Template Library (STL) that stores key-value pairs in no particular order. It uses a hash table for its internal implementation, providing average constant-time complexity for insertion, deletion, and search operations.
#include <iostream>
#include <unordered_map>
#include <string>
int main() {
std::unordered_map<std::string, int> ages;
// Inserting elements
ages["Alice"] = 30;
ages.insert({"Bob", 25});
ages.emplace("Charlie", 35);
// Accessing elements
std::cout << "Alice's age: " << ages["Alice"] << std::endl;
// Checking if a key exists
if (ages.find("David") == ages.end()) {
std::cout << "David is not in the map" << std::endl;
}
// Iterating through the map
for (const auto& pair : ages) {
std::cout << pair.first << " is " << pair.second << " years old" << std::endl;
}
// Size of the map
std::cout << "Number of entries: " << ages.size() << std::endl;
// Removing an element
ages.erase("Bob");
return 0;
}
unordered_map
with string keys and integer values is created[]
operator, insert()
, and emplace()
[]
operator is used for direct access to valuesfind()
is used to check if a key existssize()
returns the number of elementserase()
removes an element by its key#include <iostream>
#include <unordered_map>
#include <string>
struct Person {
std::string name;
int age;
bool operator==(const Person& other) const {
return name == other.name && age == other.age;
}
};
// Custom hash function for Person
struct PersonHash {
std::size_t operator()(const Person& p) const {
return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age);
}
};
int main() {
std::unordered_map<Person, std::string, PersonHash> personDescriptions;
personDescriptions[{"Alice", 30}] = "Software Engineer";
personDescriptions[{"Bob", 25}] = "Data Analyst";
personDescriptions[{"Charlie", 35}] = "Project Manager";
for (const auto& pair : personDescriptions) {
std::cout << pair.first.name << " (Age " << pair.first.age << "): "
<< pair.second << std::endl;
}
return 0;
}
Person
struct is defined with name
and age
operator==
is defined for Person
to check equalityPersonHash
is created for Person
objectsunordered_map
of Person
objects to strings is created, using PersonHash
std::map
#include <iostream>
#include <unordered_map>
#include <map>
#include <chrono>
#include <random>
#include <string>
// Function to generate random strings
std::string random_string(std::size_t length) {
const std::string characters = "abcdefghijklmnopqrstuvwxyz";
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<> distribution(0, characters.size() - 1);
std::string result;
for (std::size_t i = 0; i < length; ++i) {
result += characters[distribution(generator)];
}
return result;
}
// Function to measure execution time
template<typename Func>
long long measureTime(Func func) {
auto start = std::chrono::high_resolution_clock::now();
func();
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
int main() {
const int NUM_ELEMENTS = 1000000;
const int NUM_SEARCHES = 1000000;
std::unordered_map<std::string, int> umap;
std::map<std::string, int> map;
std::vector<std::string> keys;
for (int i = 0; i < NUM_ELEMENTS; ++i) {
keys.push_back(random_string(10));
}
// Insertion
auto insertUnordered = [&]() {
for (const auto& key : keys) {
umap[key] = 1;
}
};
auto insertOrdered = [&]() {
for (const auto& key : keys) {
map[key] = 1;
}
};
std::cout << "Insertion time (microseconds):" << std::endl;
std::cout << "unordered_map: " << measureTime(insertUnordered) << std::endl;
std::cout << "map: " << measureTime(insertOrdered) << std::endl;
// Search
auto searchUnordered = [&]() {
for (int i = 0; i < NUM_SEARCHES; ++i) {
umap.find(keys[i % NUM_ELEMENTS]);
}
};
auto searchOrdered = [&]() {
for (int i = 0; i < NUM_SEARCHES; ++i) {
map.find(keys[i % NUM_ELEMENTS]);
}
};
std::cout << "Search time (microseconds):" << std::endl;
std::cout << "unordered_map: " << measureTime(searchUnordered) << std::endl;
std::cout << "map: " << measureTime(searchOrdered) << std::endl;
return 0;
}
std::unordered_map
and std::map
measureTime
function calculates execution timestd::unordered_map
typically shows faster insertion and search times due to its hash-based implementation#include <iostream>
#include <unordered_map>
#include <string>
int main() {
std::unordered_map<std::string, int> wordCount = {
{"apple", 3}, {"banana", 2}, {"cherry", 5},
{"date", 1}, {"elderberry", 4}, {"fig", 3}
};
// Print bucket information
std::cout << "Bucket count: " << wordCount.bucket_count() << std::endl;
std::cout << "Max bucket count: " << wordCount.max_bucket_count() << std::endl;
std::cout << "Load factor: " << wordCount.load_factor() << std::endl;
std::cout << "Max load factor: " << wordCount.max_load_factor() << std::endl;
// Print contents of each bucket
for (size_t i = 0; i < wordCount.bucket_count(); ++i) {
std::cout << "Bucket " << i << " contains:";
for (auto it = wordCount.begin(i); it != wordCount.end(i); ++it) {
std::cout << " " << it->first << "(" << it->second << ")";
}
std::cout << std::endl;
}
// Find which bucket an element is in
std::string search = "cherry";
size_t bucket = wordCount.bucket(search);
std::cout << "'" << search << "' is in bucket " << bucket << std::endl;
// Rehash the container
std::cout << "\nRehashing to 20 buckets..." << std::endl;
wordCount.rehash(20);
std::cout << "New bucket count: " << wordCount.bucket_count() << std::endl;
return 0;
}
std::unordered_map
bucket_count()
returns the number of buckets in the containermax_bucket_count()
shows the maximum number of buckets the container can haveload_factor()
returns the average number of elements per bucketmax_load_factor()
returns the current maximum load factorbucket(key)
is used to find which bucket a specific element is inrehash()
is used to manually rehash the container with a new bucket countIterator Stability: Iterators and references to elements in an unordered_map
remain valid after insertion or deletion of other elements.
Rehashing: When the load factor exceeds the max load factor, the container automatically increases the number of buckets and rehashes the elements.
Custom Types: When using custom types as keys, you need to provide a hash function and an equality comparison function.
No Duplicates: unordered_map
automatically handles duplicate keys by not inserting them.
Memory Usage: unordered_map
typically uses more memory than map
due to its hash table structure, but this trade-off allows for faster access times.
std::unordered_map
is a powerful container in C++ for storing key-value pairs with fast access times. Key points to remember:
std::map
for most operations, especially with large datasetsstd::unordered_map
is ideal for scenarios where fast lookup and uniqueness of keys are required, and the order of elements is not important. It's commonly used in situations like:
- Implementing caches
- Counting occurrences of elements
- Fast key-value lookups in large datasets
- Implementing certain algorithms that require quick element access
Understanding when to use std::unordered_map
versus other containers like std::map
or std::vector
is crucial for writing efficient and clear C++ code in various application domains. Its constant-time average complexity for key operations makes it a go-to choice for many performance-critical applications.