⭐Blog homepage: ️CS semi homepage
⭐Welcome to follow: like, favorite and leave a message
⭐ Column series: Advanced C++
⭐Code repository: Advanced C++
Home It is not easy for people to update. Your likes and attention are very important to me. Friends, please like and follow me. Your support is the biggest motivation for my creation. Friends are welcome to send private messages to ask questions. Family members, don’t forgetLike and collect + follow! ! !
Introduction and use of unordered_set and unordered_map
1. Unordered series of associative containers
In C++98, STL providesa series of associative containers with a red-black tree structure at the bottom layer, which is efficient in querying Achievable l o g 2 N log_2 N log2N, that is, in the worst case, the height of the red-black tree needs to be compared. When there are many nodes in the tree, the query efficiency is not ideal. The best query is that can find the element with a small number of comparisons, so in C++11, STL provides 4 more The unordered series of associative containers, these four containers are basically similar to the associative containers of red-black tree structure, except that their underlying structures are different.
二、unordered_map and unordered_multimap
1. Introduction to unordered_map
- unordered_map is an associative container that stores <key, value> key-value pairs, which allows fast indexing to the corresponding keys. value.
- In unordered_map, the key value is usually used to uniquely identify the element, while the map value is an object whose content is associated with this key. Keys and mapped values may be of different types.
- Internally,unordered_map does not sort <kye, value> in any specific order, in order to find the key within a constant range Corresponding value, unordered_map puts key-value pairs with the same hash value in the same bucket.
- unordered_map containerAccessing a single element by key is faster than map, but it usually traverses the elements Inefficient range iteration of subsets.
- unordered_maps implements the direct access operator (operator[]), which allows direct access to value using key as parameter.
- Its iterator is at leasta forward iterator.
2. Use of unordered_map
(1) Definition
It is defined as follows:
void test_unordered_map1()
{
// 构造一个空的key为int,value为double的unordered_map
unordered_map<int, double> um1;
// 给um1赋上值
um1.insert(make_pair(1, 1.1));
um1.insert(make_pair(2, 2.2));
um1.insert(make_pair(3, 3.3));
um1.insert(make_pair(4, 4.4));
// 拷贝构造
unordered_map<int, double> um2(um1);
// 迭代器区间拷贝um2的一段
unordered_map<int, double> um3(um2.begin(), um2.end());
// for循环打印一下um3,um3没问题则um1和um2都没问题
for (auto& e : um3)
{
cout << e.first<< "=>" << e.second << " ";
}
cout << endl;
}
(2) Interface usage
member function | Function |
---|---|
insert | Insert key-value pair |
erase | Delete the key-value pair with the value of the specified key |
size | Get the number of elements in the container |
find | Find key-value pairs with specified key value |
empty | Determine whether the container is empty |
clear | Clear current container |
swap | Exchange data in two containers |
count | Get the number of elements with the specified key value in the container |
[] | The [] function of operator overloading is very powerful, including insertion, modification, search, etc. |
begin() | Get the forward iterator of the first element in the container |
end() | Gets the forward iterator of the next element of the last element in the container |
Key points[]:
1. If there is already a key-value pair with the key value key in the current container, thenReturns a reference to value for this key.
2. If there is no key-value pair with the key value key in the current container, firstInsert key-value pair <key, value()>, and then returns a reference to the value in the key-value pair.
Let’s look directly at the code, regarding all the above code operations:
void test_unordered_map2()
{
// 构造一个空的key为int,value为string的unordered_map
unordered_map<int, string> um1;
// 插入方法一:构造匿名对象插入
um1.insert(pair<int, string>(1, "111"));
um1.insert(pair<int, string>(2, "222"));
um1.insert(pair<int, string>(3, "333"));
// 插入方法二:调用make_pair插入
um1.insert(make_pair(4, "444"));
um1.insert(make_pair(5, "555"));
um1.insert(make_pair(6, "666"));
// 插入方法三:用operator[]
um1[7] = "777";
um1[8] = "888";
um1[9] = "999";
um1[10] = "000";
// 遍历方式一:利用迭代器进行遍历打印
//unordered_map<int, string>::iterator it = um1.begin();
auto it = um1.begin();
while (it != um1.end())
{
cout << (*it).first << "=>" << (*it).second << " ";
++it;
}
cout << endl; // 1=>111 2=>222 3=>333 4=>444 5=>555 6=>666 7=>777 8=>888 9=>999 10=>000
// 遍历方法二:利用for循环进行遍历打印
for (auto& e : um1)
{
cout << e.first<< "=>" << e.second << " ";
}
cout << endl; // 1=>111 2=>222 3=>333 4=>444 5=>555 6=>666 7=>777 8=>888 9=>999 10=>000
// 删除操作1:根据键值对key删除
um1.erase(5);
// 删除操作2:根据迭代器进行删除
unordered_map<int, string>::iterator rit = um1.find(7); // 顺带使用键值对key就可以用find函数了
if (rit != um1.end())
{
um1.erase(rit);
}
// 遍历打印一下,用for循环方便快捷一点
for (auto& e : um1)
{
cout << e.first << "=>" << e.second << " ";
}
cout << endl; // 1=>111 2=>222 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
// 修改键值对:通过find获得迭代器进行修改
auto pos = um1.find(1);
if (pos != um1.end())
{
pos->second = "11/11";
}
// 修改键值对:通过operator[]运算符重载进行修改
um1[2] = "22/22";
// 打印一下
for (auto& e : um1)
{
cout << e.first << "=>" << e.second << " ";
}
cout << endl; // 1=>11/11 2=>22/22 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
// 判空
cout << um1.empty() << endl; // 0 -- 不空
// 计算容器的大小
cout << um1.size() << endl; // 8个
// 计算容器中键值对的大小
cout << um1.count(3) << endl; // 1
// 交换两容器中的数据
unordered_map<int, string> tmp{
{
11, "123"}, {
22, "345" }};
um1.swap(tmp);
for (auto& e : tmp)
{
cout << "tmp=>" << " ";
cout << e.first << "=>" << e.second << " ";
}
cout << endl; // tmp=> 1=>11/11 2=>22/22 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
for (auto& e : um1)
{
cout << "um1=>" << " ";
cout << e.first << "=>" << e.second << " ";
}
cout << endl; // um1=> 11=>123 22=>345
// 清空
um1.clear();
for (auto& e : um1)
{
cout << e.first << "=>" << e.second << " ";
}
cout << endl;
}
3、unordered_multimap
This container is basically the same as unordered_map. The difference between the two is that multimap allows the redundancy of key-value pairs, that is, it allows key and value to have different values.
void test_unordered_map3()
{
unordered_multimap<int, string> ummp1;
ummp1.insert(make_pair(2023, "yes"));
ummp1.insert(make_pair(2023, "no"));
ummp1.insert(make_pair(2023, "before"));
ummp1.insert(make_pair(2023, "now"));
for (auto& e : ummp1)
{
cout << e.first << "=>" << e.second << " ";
}
cout << endl;
}
There are three more differences:
1. The find function of unordered_map and unordered_multimap:
find function | Function |
---|---|
unordered_map | Returns an iterator of key-value pairs whose key value is key |
unordered_multimap | Returns an iterator over the first key-value pair found in the underlying hash table with key value key |
2. Count function function
count function | Function |
---|---|
unordered_map | If the key-value pair whose key value is key exists, 1 is returned, and if it does not exist, 0 is returned (the find member function can be replaced) |
unordered_multimap | Returns the number of key-value pairs whose key value is key (find member function cannot be replaced) |
3. operator[] function function
We do not have this operator[] overload in unordered_multimap, because this container can be redundant, so we are not sure which one we are looking for, which will cause a lot of errors, so our unordered_multimap does not have operator[] of!
二、unordered_set and unordered_multiset
1. Introduction to unordered_set
1. unordered_set is an associative container that stores key values in no specific order, which allows quick indexing to the corresponding elements through key values.
2. In unordered_set, the value of an element is also the key that uniquely identifies it.
3. Internally, the elements in unordered_set are not sorted in any specific order. In order to find the specified key within a constant range, unordered_set places key values with the same hash value in the same in the barrel.
4. The unordered_set container accesses a single element through key faster than set, but it is usually less efficient in range iteration over a subset of elements.
5. Its iterator is at least a forward iterator.
2. Use of unordered_set
(1) Definition
void test_unordered_set1()
{
// 构造一个空壳的us1的unordered_set的容器
unordered_set<int> us1;
// 插入几个值
us1.insert(1);
us1.insert(2);
us1.insert(3);
us1.insert(4);
// 拷贝构造
unordered_set<int> us2(us1);
// 迭代器区间构造
unordered_set<int> us3(us2.begin(), us2.end());
// for循环打印一下
for (auto& e : us3)
{
cout << e << " ";
}
cout << endl;
}
(2) Interface usage
member function | Function |
---|---|
insert | Insert specified element |
erase | Delete specified element |
size | Get the number of elements in the container |
find | Find the specified element |
empty | Determine whether the container is empty |
clear | Clear current container |
swap | Exchange data in two containers |
count | Get the number of specified elements in the container |
[] | The [] function of operator overloading is very powerful, including insertion, modification, search, etc. |
begin() | Get the forward iterator of the first element in the container |
end() | Gets the forward iterator of the next element of the last element in the container |
void test_unordered_set2()
{
// 先构造一个空的容器
unordered_set<int> us1;
// 插入元素(只有这一种插入法)
us1.insert(1);
us1.insert(2);
us1.insert(3);
us1.insert(1);
us1.insert(4);
us1.insert(5);
// 遍历容器第一种方法:迭代器遍历
unordered_set<int>::iterator it = us1.begin();
while (it != us1.end())
{
cout << *it << " ";
++it;
}
cout << endl; // 1 2 3 4 5
// 遍历容器第二种方法:for循环
for (auto& e : us1)
{
cout << e << " ";
}
cout << endl; // 1 2 3 4 5
// 删除元素的方式一:直接找到值进行删除
us1.erase(1);
// 删除元素的方法二:利用迭代器进行删除
unordered_set<int>::iterator pos = us1.find(2);
if (pos != us1.end())
{
us1.erase(pos);
}
// 打印一下
for (auto& e : us1)
{
cout << e << " ";
}
cout << endl; // 3 4 5
// 判断容器是否为空
cout << us1.empty() << endl; // 0
// 获取值为3的个数
cout << us1.count(3) << endl; // 1
// 查看当前容器的容量
cout << us1.size() << endl; // 3
// 交换数据
unordered_set<int> tmp{
99, 88, 77, 66};
us1.swap(tmp);
// 打印一下
for (auto& e : us1)
{
cout << e << " ";
}
cout << endl; // 99 88 77 66
// 打印一下
for (auto& e : tmp)
{
cout << e << " ";
}
cout << endl; // 3 4 5
// 清空
us1.clear();
// 打印一下
for (auto& e : us1)
{
cout << e << " ";
}
cout << endl; //
}
3、unordered_multiset
The roughly implemented function is the same as unordered_map, but the only difference is that this multi-functional set allows values to be repeated!
void test_unordered_set3()
{
unordered_multiset<int> ums1;
ums1.insert(1);
ums1.insert(2);
ums1.insert(4);
ums1.insert(3);
ums1.insert(1);
ums1.insert(5);
ums1.insert(2);
ums1.insert(7);
for (auto& e : ums1)
{
cout << e << " ";
}
cout << endl; // 1 1 2 2 3 4 5 7
}
Compared with the ordinary set, the count function of this multi-functional set returns the number of items. The count function of the ordinary set returns 1 if it exists and 0 if it does not exist.
Compared with the ordinary set, the find function of this multi-functional set returns the iterator of the first element found with the key value val in the underlying hash table, while the ordinary set returns a simple key.
3. The difference between map/set and unordered_map/unordered_set
Let’s do some performance testing:
#include <iostream>
#include <set>
#include <unordered_set>
#include <time.h>
using namespace std;
int main()
{
int N = 1000;
vector<int> v;
v.reserve(N);
srand((unsigned int)time(NULL));
//随机生成N个数字
for (int i = 0; i < N; i++)
{
v.push_back(rand());
}
//将这N个数插入set容器
set<int> s;
clock_t begin1 = clock();
for (auto e : v)
{
s.insert(e);
}
clock_t end1 = clock();
//将这N个数插入unordered_set容器
unordered_set<int> us;
clock_t begin2 = clock();
for (auto e : v)
{
us.insert(e);
}
clock_t end2 = clock();
//分别输出插入set容器和unordered_set容器所用的时间
cout << "set insert: " << end1 - begin1 << endl;
cout << "unordered_set insert: " << end2 - begin2 << endl;
//在set容器中查找这N个数
clock_t begin3 = clock();
for (auto e : v)
{
s.find(e);
}
clock_t end3 = clock();
//在unordered_set容器中查找这N个数
clock_t begin4 = clock();
for (auto e : v)
{
us.find(e);
}
clock_t end4 = clock();
//分别输出在set容器和unordered_set容器中查找这N个数所用的时间
cout << "set find: " << end3 - begin3 << endl;
cout << "unordered_set find: " << end4 - begin4 << endl;
//将这N个数从set容器中删除
clock_t begin5 = clock();
for (auto e : v)
{
s.erase(e);
}
clock_t end5 = clock();
//将这N个数从unordered_set容器中删除
clock_t begin6 = clock();
for (auto e : v)
{
us.erase(e);
}
clock_t end6 = clock();
//分别输出将这N个数从set容器和unordered_set容器中删除所用的时间
cout << "set erase: " << end5 - begin5 << endl;
cout << "unordered_set erase: " << end6 - begin6 << endl;
return 0;
}