std::map
是C++标准模板库(STL)中的关联容器,它提供了一种将键和值一一对应关联起来的数据结构。
其中的元素按照键的顺序进行排序,默认情况下是按照键的升序排序。
每个键只能出现一次,如果要插入具有相同键的新元素,则会覆盖原有键对应的值。
std::map
的底层实现通常基于红黑树(Red-Black Tree)。红黑树是一种自平衡的二叉搜索树,它满足以下性质:
1、 每个节点要么是红色,要么是黑色。
2、 根节点是黑色的。
3、 每个叶子节点(NIL节点,空节点)是黑色的。
4、 如果一个节点是红色的,则它的两个子节点都是黑色的。
5、 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,黑色节点的数量相同。
红黑树的这些性质保证了树的平衡性,使得查找、插入和删除操作的时间复杂度都能保持在O(log n)级别。
在std::map
中,每个元素都存储为一个键值对,键和值之间是一一对应的关系。在红黑树中,每个节点包含一个键值对和指向左右子节点的指针。元素按照键的大小顺序排列,因此红黑树的节点是按照键的大小顺序排列的。
当执行插入、删除或查找操作时,红黑树会根据节点的键进行自平衡操作,以保持树的平衡性。这些自平衡操作包括颜色翻转、左旋和右旋等,通过这些操作,红黑树可以保持平衡,同时保持较高的性能。
1、 构造函数:
map()
:默认构造函数,创建一个空的std::map
。
map(InputIterator first, InputIterator last)
:根据指定范围的元素构造map。
map(const map& other)
:复制构造函数,创建一个新的map,包含另一个map的所有元素。
std::map<std::string,int> map1;
map1.insert({"A",1});
map1.insert({"B",2});
map1.insert({"C",3});
map1.insert({"D",4});
std::map<std::string,int> map2(map1); // 4
std::cout<<map2.size()<<std::endl;
std::map<std::string,int> map3(std::move(map2));
std::cout<<map2.size()<<std::endl; // 0
std::cout<<map3.size()<<std::endl; // 4
2、 容量查询:
empty()
:检查map是否为空。
size()
:返回map中元素的个数。
max_size()
:返回map支持的最大元素数量。
std::map<std::string,int> map1;
map1.insert({"A",1});
map1.insert({"B",2});
map1.insert({"C",3});
map1.insert({"D",4});
std::cout<<map1.size()<<std::endl; // 4
std::cout<<map1.empty()<<std::endl; // 0
std::cout<<map1.max_size()<<std::endl; // 128102389400760775
3、 插入和删除:
insert(const value_type& value)
:插入一个键值对。
emplace(Args&&、、、 args)
:在map中原位构造一个新元素。
erase(const key_type& key)
:删除指定键对应的元素。
clear()
:清空map中的所有元素。
Demo:insert函数演示
#include
#include
int main() {
// 创建一个空的std::map
std::map<int, std::string> myMap;
// 单个元素插入
auto result1 = myMap.insert(std::make_pair(1, "one"));
if (result1.second) {
std::cout << "Inserted: " << result1.first->first << " -> " << result1.first->second << std::endl;
} else {
std::cout << "Element already exists: " << result1.first->first << " -> " << result1.first->second << std::endl;
}
// 通过移动元素插入
auto result2 = myMap.insert(std::make_pair(2, "two"));
if (result2.second) {
std::cout << "Inserted: " << result2.first->first << " -> " << result2.first->second << std::endl;
} else {
std::cout << "Element already exists: " << result2.first->first << " -> " << result2.first->second << std::endl;
}
// 范围插入
std::map<int, std::string> anotherMap = {{3, "three"}, {4, "four"}};
myMap.insert(anotherMap.begin(), anotherMap.end());
// 通过位置插入
std::map<int, std::string>::iterator it = myMap.find(2); // 找到键为2的位置
if (it != myMap.end()) {
myMap.insert(it, std::make_pair(5, "five")); // 在键为2的位置之前插入
}
// 通过列表初始化插入
auto result5 = myMap.insert(std::make_pair(6, "six"));
if (result5.second) {
std::cout << "Inserted: " << result5.first->first << " -> " << result5.first->second << std::endl;
} else {
std::cout << "Element already exists: " << result5.first->first << " -> " << result5.first->second << std::endl;
}
// 输出map中的所有元素
std::cout << "Map contents:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << " -> " << pair.second << std::endl;
}
return 0;
}
Demo:erase函数演示
#include
#include
int main() {
// 创建一个 std::map 对象
std::map<int, std::string> myMap;
// 添加一些键值对
myMap[1] = "One";
myMap[2] = "Two";
myMap[3] = "Three";
myMap[4] = "Four";
myMap[5] = "Five";
// 显示原始的 std::map
std::cout << "Original Map:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 删除键值为2的元素
myMap.erase(2);
// 显示删除后的 std::map
std::cout << "\nMap after erasing key 2:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 删除迭代器位置为1的元素
auto it = myMap.find(4);
if (it != myMap.end()) {
myMap.erase(it);
}
// 显示删除后的 std::map
std::cout << "\nMap after erasing iterator position 1:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 删除指定范围内的元素
auto first = myMap.lower_bound(1);
auto last = myMap.upper_bound(3);
myMap.erase(first, last);
// 显示删除后的 std::map
std::cout << "\nMap after erasing elements in range [1, 3):" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
4、 查找和访问:
find(const key_type& key)
:查找指定键的元素,返回指向该元素的迭代器。
count(const key_type& key)
:返回指定键在map中出现的次数(0或1)。
at(const key_type& key)
:返回指定键对应的值。
operator[]
:重载了下标访问运算符,返回给定键对应的值的引用。
std::map<std::string,int> map1;
map1.insert({"A",1});
map1.insert({"B",2});
map1.insert({"C",3});
map1.insert({"D",4});
auto it = map1.find("3");
for(auto num : map1){
std::cout<<num.first<<" "<<num.second<<std::endl; // A
}
// A 1
// B 2
// C 3
// D 4
std::cout<<map1.count("A")<<std::endl; // 1
std::cout<<map1.at("A")<<std::endl; // 1
std::cout<<map1["A"]<<std::endl; // 1
5、 迭代器相关:
begin()
:返回指向第一个元素的迭代器。
end()
:返回指向尾后元素的迭代器。
rbegin()
:返回指向最后一个元素的逆向迭代器。
rend()
:返回指向首个元素前一个位置的逆向迭代器。
6、 赋值和交换:
operator=
:赋值运算符,将一个map的内容复制给另一个map。
swap(map& other)
:交换两个map的内容。
std::map<std::string,int> map1;
map1.insert({"A",1});
map1.insert({"B",2});
map1.insert({"C",3});
map1.insert({"D",4});
std::map<std::string,int> map2;
map1.swap(map2);
std::cout<<map1.size()<<std::endl; // 0
std::cout<<map2.size()<<std::endl; // 4
当使用自定义对象作为std::map
的键时,需要确保自定义对象可以进行比较。这通常涉及到重载比较运算符(例如operator<
)以确保元素可以按照特定的顺序进行排序。
Demp定义一个自定义对象的std::map
:
#include
#include
// 自定义对象
class MyObject {
public:
int id;
std::string name;
MyObject(int id, const std::string& name) : id(id), name(name) {}
// 重载比较运算符,以确保对象可以按照特定顺序进行排序
bool operator<(const MyObject& other) const {
return id < other.id;
}
};
int main() {
// 创建一个存储自定义对象的 std::map
std::map<MyObject, std::string> myMap;
// 添加自定义对象到 map
myMap.emplace(MyObject(1, "Object1"), "Value1");
myMap.emplace(MyObject(3, "Object3"), "Value3");
myMap.emplace(MyObject(2, "Object2"), "Value2");
// 遍历 map,并输出每个键值对
for (const auto& pair : myMap) {
std::cout << "Key: " << pair.first.id << ", Value: " << pair.second << std::endl;
}
return 0;
}
std::map
的性能受到多种因素的影响,包括但不限于以下几点:
1、 底层数据结构:std::map
通常基于红黑树实现,这确保了在插入、查找和删除操作上有较好的平衡性能。
2、 插入和删除操作:由于std::map
是有序的关联容器,插入和删除操作可能需要重新平衡底层的红黑树,因此插入和删除元素的性能可能略低于无序容器,例如std::unordered_map
。
3、 查找操作:由于底层使用了红黑树,std::map
的查找操作的时间复杂度为O(log n),其中n是容器中元素的数量。这使得在大型数据集上查找效率较高。
4、 内存占用:红黑树作为底层数据结构,每个节点需要存储额外的指针和颜色信息,因此std::map
相比于一些无序容器可能会消耗更多的内存。
5、 迭代器稳定性:std::map
的迭代器在插入和删除操作后仍然有效,这意味着不会因为插入或删除元素而使现有的迭代器失效。
综上所述,std::map
在大多数情况下提供了较好的性能和稳定性,特别适用于需要有序存储和高效查找的场景。然而,在某些特定情况下,例如需要快速的插入和删除操作,并且不需要元素的顺序关系时,可能会有更合适的选择。