概述
std::map是 C++ 标准模板库(STL)中的一个关联容器,它存储键值对(key-value pairs),并根据键(key)自动排序。std::map通常基于红黑树(一种自平衡二叉搜索树)实现,因此能保证对数时间复杂度的查找、插入和删除操作。
基本特性
头文件:
<map>定义:
template <class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class map;键唯一性:每个键在
map中只能出现一次排序:元素始终按键的升序排列(默认使用
std::less<Key>)时间复杂度:
查找:O(log n)
插入:O(log n)
删除:O(log n)
基本用法
1. 创建和初始化
#include <iostream> #include <map> #include <string> int main() { // 空map std::map<int, std::string> map1; // 使用初始化列表 std::map<int, std::string> map2 = { {1, "Alice"}, {2, "Bob"}, {3, "Charlie"} }; // 复制构造 std::map<int, std::string> map3(map2); // 范围构造 std::map<int, std::string> map4(map2.begin(), map2.end()); return 0; }2. 插入元素
std::map<int, std::string> myMap; // 使用insert myMap.insert({1, "Apple"}); myMap.insert(std::make_pair(2, "Banana")); myMap.insert(std::pair<const int, std::string>(3, "Cherry")); // 使用下标运算符(如果键不存在则插入) myMap[4] = "Date"; // 插入键4,值为"Date" myMap[5] = "Elderberry"; // 使用emplace(C++11及以上) myMap.emplace(6, "Fig");3. 访问元素
std::map<int, std::string> myMap = {{1, "One"}, {2, "Two"}, {3, "Three"}}; // 使用下标运算符(注意:如果键不存在,会创建新元素) std::cout << myMap[1] << std::endl; // 输出: One // 使用at(如果键不存在,抛出std::out_of_range异常) try { std::cout << myMap.at(2) << std::endl; // 输出: Two } catch (const std::out_of_range& e) { std::cout << "Key not found!" << std::endl; } // 使用find(安全的方式) auto it = myMap.find(3); if (it != myMap.end()) { std::cout << it->second << std::endl; // 输出: Three }4. 删除元素
std::map<int, std::string> myMap = { {1, "A"}, {2, "B"}, {3, "C"}, {4, "D"}, {5, "E"} }; // 通过迭代器删除 auto it = myMap.find(2); if (it != myMap.end()) { myMap.erase(it); } // 通过值删除 myMap.erase(3); // 删除一个范围 myMap.erase(myMap.find(4), myMap.end()); // 删除所有元素 myMap.clear();5. 遍历元素
std::map<int, std::string> myMap = {{1, "Red"}, {2, "Green"}, {3, "Blue"}}; // 使用迭代器 for (auto it = myMap.begin(); it != myMap.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } // 使用范围for循环(C++11及以上) for (const auto& pair : myMap) { std::cout << pair.first << ": " << pair.second << std::endl; } // 使用结构化绑定(C++17及以上) for (const auto& [key, value] : myMap) { std::cout << key << ": " << value << std::endl; }容量查询
std::map<int, std::string> myMap = {{1, "A"}, {2, "B"}}; // 获取元素数量 std::cout << "Size: " << myMap.size() << std::endl; // 输出: 2 // 检查是否为空 std::cout << "Empty: " << std::boolalpha << myMap.empty() << std::endl; // 输出: false // 获取最大可能的大小 std::cout << "Max size: " << myMap.max_size() << std::endl;查找操作
std::map<int, std::string> myMap = {{1, "A"}, {2, "B"}, {3, "C"}}; // find - 查找键,返回迭代器 auto it = myMap.find(2); if (it != myMap.end()) { std::cout << "Found: " << it->second << std::endl; } // count - 检查键是否存在(对于map,返回0或1) if (myMap.count(3) > 0) { std::cout << "Key 3 exists" << std::endl; } // lower_bound - 返回第一个不小于key的元素的迭代器 auto lb = myMap.lower_bound(2); if (lb != myMap.end()) { std::cout << "Lower bound: " << lb->first << " -> " << lb->second << std::endl; } // upper_bound - 返回第一个大于key的元素的迭代器 auto ub = myMap.upper_bound(2); if (ub != myMap.end()) { std::cout << "Upper bound: " << ub->first << " -> " << ub->second << std::endl; } // equal_range - 返回匹配键的范围 auto range = myMap.equal_range(2); for (auto i = range.first; i != range.second; ++i) { std::cout << i->first << " -> " << i->second << std::endl; }自定义比较函数
#include <iostream> #include <map> #include <string> // 自定义比较函数 - 按字符串长度排序 struct StringLengthCompare { bool operator()(const std::string& a, const std::string& b) const { return a.length() < b.length(); } }; int main() { // 使用自定义比较函数 std::map<std::string, int, StringLengthCompare> lengthMap; lengthMap.insert({"apple", 5}); lengthMap.insert({"kiwi", 4}); lengthMap.insert({"banana", 6}); for (const auto& pair : lengthMap) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; }性能注意事项
时间复杂度:
查找、插入、删除:O(log n)
遍历:O(n)
空间复杂度:O(n),每个元素需要额外的指针空间(通常每个节点需要3个指针)
与
std::unordered_map对比:std::map:有序,基于红黑树,O(log n)操作std::unordered_map:无序,基于哈希表,平均O(1)操作,最坏O(n)
使用场景:
需要有序遍历
元素数量相对稳定
键的比较操作开销不大
内存使用不是主要限制
线程安全性
多个线程可以同时读取同一个
std::map对象如果有任何线程修改
std::map,必须确保没有其他线程同时访问该对象对于多线程环境,考虑使用互斥锁或并发容器(如Intel TBB的
concurrent_hash_map)
最佳实践
避免频繁的插入删除:如果频繁操作,考虑
std::unordered_map使用
emplace替代insert:避免不必要的临时对象创建使用
find而不是operator[]检查存在性:operator[]会插入不存在的键考虑使用
std::unordered_map:当不需要有序遍历时使用
auto简化迭代器声明:提高代码可读性
示例应用
#include <iostream> #include <map> #include <string> class StudentDatabase { private: std::map<int, std::string> students; public: void addStudent(int id, const std::string& name) { auto result = students.emplace(id, name); if (!result.second) { std::cout << "Student with ID " << id << " already exists!" << std::endl; } } bool removeStudent(int id) { return students.erase(id) > 0; } std::string getStudentName(int id) const { auto it = students.find(id); if (it != students.end()) { return it->second; } return "Student not found"; } void listAllStudents() const { if (students.empty()) { std::cout << "No students in database." << std::endl; return; } std::cout << "Student List (sorted by ID):" << std::endl; for (const auto& [id, name] : students) { std::cout << "ID: " << id << ", Name: " << name << std::endl; } } size_t getStudentCount() const { return students.size(); } }; int main() { StudentDatabase db; db.addStudent(1001, "Alice Johnson"); db.addStudent(1003, "Bob Smith"); db.addStudent(1002, "Charlie Brown"); db.listAllStudents(); std::cout << "\nStudent 1002: " << db.getStudentName(1002) << std::endl; db.removeStudent(1002); std::cout << "\nAfter removing student 1002:" << std::endl; db.listAllStudents(); return 0; }总结
std::map是C++中一个功能强大且灵活的关联容器,提供了高效的键值对存储和检索能力。它的自动排序特性使得有序遍历变得简单高效。在选择使用std::map时,应根据具体需求权衡其有序性和对数时间复杂度的特性与其他容器(如std::unordered_map)的差异。