C++ STL 无序容器std::unordered_map

文章目录

    • std::unordered_map
    • 创建 unordered_map 容器
    • 成员方法
    • 无序容器的底层实现机制
    • 成员方法
    • 获取元素的 4 种方法
    • insert() 用法
    • emplace() 和 emplace_hint() 方法
    • 删除元素 erase()/clear()
    • erase()
    • clear()


std::unordered_map

unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的。

unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而unordered_map 容器中是无序的。

template < class Key,               // 键值对中键的类型
           class T,                 // 键值对中值的类型
           class Hash = hash<Key>,  //容器内部存储键值对所用的哈希函数
           class Pred = equal_to<Key>,  // 判断各个键值对键相同的规则
           class Alloc = allocator< pair<const Key,T> >  // 指定分配器对象的类型
           > class unordered_map;

参数 含义
前 2 个参数分别用于确定键值对中键和值的类型,也就是存储键值对的类型。
Hash = hash 用于指明容器在存储各个键值对时要使用的哈希函数,默认使用 STL 标准库提供的 hash 哈希函数。注 意,默认哈希函数只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。
Pred = equal_to unordered_map 容器中存储的键不能相等,判断是否相等的规则,就由此参数指定。默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。

创建 unordered_map 容器

默认构造函数

std::unordered_map<std::string, std::string> umap;

创建的同时初始化

std::unordered_map<std::string, std::string> umap{
      {"Python 教程","http://c.biancheng.net/python/"},
      {"Java 教程","http://c.biancheng.net/java/"},
      {"Linux 教程","http://c.biancheng.net/linux/"} };

拷贝/移动构造函数

// 拷贝构造函数
std::unordered_map<std::string, std::string> umap2(umap);
// 移动构造函数
// 返回临时 unordered_map 容器的函数
std::unordered_map <std::string, std::string > retUmap(){
      std::unordered_map<std::string, std::string>tempUmap{
          {"Python 教程","http://c.biancheng.net/python/"},
          {"Java 教程","http://c.biancheng.net/java/"},
          {"Linux 教程","http://c.biancheng.net/linux/"} };
      return tempUmap;
}
// 调用移动构造函数,创建 umap2 容器
std::unordered_map<std::string, std::string> umap2(retUmap());

选择已有容器部分区域创建

//传入 2 个迭代器,
std::unordered_map<std::string, std::string> umap2(++umap.begin(),umap.end());

成员方法

成员方法 功能
begin() 返回指向容器中第一个键值对的正向迭代器。
end() 返回指向容器中最后一个键值对之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前容器中存有键值对的个数。
max_size() 返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
operator[key] 该模板类中重载了 [] 运算符,其功能是可以向访问数组中元素那样,只要给定某个键值对的键 key,就可以获取该键对应的值。注意,如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对。
at(key) 返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。
find(key) 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找以 key 键的键值对的个数。
equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。
emplace() 向容器中添加新键值对,效率比 insert() 方法高。
emplace_hint() 向容器中添加新键值对,效率比 insert() 方法高。
insert() 向容器中添加新键值对。
erase() 删除指定键值对。
clear() 清空容器,即删除容器中存储的所有键值对。
swap() 交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。
bucket_count() 返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count() 返回当前系统中,unordered_map 容器底层最多可以使用多少桶。
bucket_size(n) 返回第 n 个桶中存储键值对的数量。
bucket(key) 返回以 key 为键的键值对所在桶的编号。
load_factor() 返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者设置当前 unordered_map 容器的负载因子。
rehash(n) 将当前容器底层使用桶的数量设置为 n。
reserve() 将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳 count 个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function() 返回当前容器使用的哈希函数对象。

对于实现互换 2 个相同类型 unordered_map 容器的键值对,除了可以调用该容器模板类中提供的
swap() 成员方法外,STL 标准库还提供了同名的 swap() 非成员函数。

无序容器的底层实现机制

C++ STL 标准库中,不仅是 unordered_map 容器,所有无序容器的底层实现都采用的是哈希表存储结构。更准确地说,是用“链地址法”(又称“开链法”)解决数据存储位置发生冲突的哈希表。

当使用无序容器存储键值对时,会先申请一整块连续的存储空间,但此空间并不用来直接存储键值对,而是存储各个链表的头指针,各键值对真正的存储位置是各个链表的节点。 STL 标准库通常选用 vector 容器存储各个链表的头指针。

当有新键值对存储到无序容器中时,整个存储过程分为好几步:

将该键值对中键的值带入设计好的哈希函数,会得到一个哈希值(一个整数,用 H 表示);

将 H 和无序容器拥有桶的数量 n 做整除运算(即 H % n),该结果即表示应将此键值对存储到的桶的编号;

建立一个新节点存储此键值对,同时将该节点链接到相应编号的桶上。 哈希表存储结构有一个重要的属性,称为负载因子(load factor)。该属性同样适用于无序容器,用于衡量容器存储键值对的空/满程序,即负载因子越大,意味着容器越满,即各链表中挂载着越多的键值对,这无疑会降低容器查找目标键值对的效率;反之,负载因子越小,容器肯定越空,但并不一定各个链表中挂载的键值对就越少。

无序容器中,负载因子的计算方法为: 负载因子 = 容器存储的总键值对 / 桶数

默认情况下,无序容器的最大负载因子为 1.0。如果操作无序容器过程中,使得最大复杂因子超过了默认值,则容器会自动增加桶数,并重新进行哈希,以此来减小负载因子的值。需要注意的是,此过程会导致容器迭代器失效,但指向单个键值对的引用或者指针仍然有效。

无序容器管理哈希表的成员方法

成员方法 功能
bucket_count() 返回当前容器底层存储键值对时,使用桶的数量。
max_bucket_count() 返回当前系统中,unordered_map 容器底层最多可以使用多少个桶。
bucket_size(n) 返回第 n 个桶中存储键值对的数量。
bucket(key) 返回以 key 为键的键值对所在桶的编号。
load_factor() 返回 unordered_map 容器中当前的负载因子。
max_load_factor() 返回或者设置当前 unordered_map 容器的最大负载因子。
rehash(n) 尝试重新调整桶的数量为等于或大于 n 的值。如果 n 大于当前容器使用的桶数,则该方法会是容器重新哈希, 该容器新的桶数将等于或大于 n。反之,如果 n 的值小于当前容器使用的桶数,则调用此方法可能没有任何作用。
reserve(n) 将容器使用的桶数(bucket_count() 方法的返回值)设置为最适合存储 n 个元素的桶数。
hash_function() 返回当前容器使用的哈希函数对象。

成员方法

成员方法 功能
begin() 返回指向容器中第一个键值对的正向迭代器。
end() 返回指向容器中最后一个键值对之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
find(key) 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后 一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。
equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。
equal_range(key) 很少用,因为该容器中存储的键值都不相等

获取元素的 4 种方法

利用下标访问普通数组中元素,如果没有则添加

// 创建 umap 容器 
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"}, 
    {"Linux 教程","http://c.biancheng.net/linux/"} }; 

// 获取 "Java 教程" 对应的值
string str = umap["Java 教程"];

// 添加
umap["C 教程"] = "http://c.biancheng.net/c/";
at() 成员方法,查找失败会报错
//创建 umap 容器 
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"}, 
    {"Linux 教程","http://c.biancheng.net/linux/"} };
//获取指定键对应的值
string str = umap.at("Python 教程");
find() 成员方法。成功返回指向该键值对的迭代器,失败返回 end() 方法一致的迭代器,指向最后一个键值对之后位置。
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"},
    {"Linux 教程","http://c.biancheng.net/linux/"} };

unordered_map<string, string>::iterator iter = umap.find("Python 教程");
unordered_map<string, string>::iterator iter2 = umap.find("GO 教程");   // 查找失败
if (iter2 == umap.end()) 
    cout << "当前容器中没有以\"GO 教程\"为键的键值对";

通过 begin()/end() 或者 cbegin()/cend() 遍历

insert() 用法

insert() 方法可以向已建 unordered_map 容器中添加新的键值对。根据功能的不同,共有四种用法。

将 pair 类型键值对添加到容器中

// 以普通方式传递参数 
pair<iterator,bool> insert ( const value_type& val ); 
// 以右值引用的方式传递参数 
template <class P>
  pair<iterator,bool> insert ( P&& val );

返回 pair 类型值,内部包含一个 iterator 迭代器和 bool 变量,添加成功 bool 为 True,添加失败 bool 为 False。

// 创建空 umap 容器 
unordered_map<string, string> umap; 
// 构建要添加的键值对 
std::pair<string, string>mypair("STL 教程", "http://c.biancheng.net/stl/"); 
// 创建接收 insert() 方法返回值的 pair 类型变量 
std::pair<unordered_map<string, string>::iterator, bool> ret; 
// 调用 insert() 方法的第一种语法格式
ret = umap.insert(mypair);
// 调用 insert() 方法的第二种语法格式
ret = umap.insert(std::make_pair("Python 教程","http://c.biancheng.net/python/"));
指定新键值对要添加到容器中的位置
// 以普通方式传递 val 参数 
iterator insert ( const_iterator hint, const value_type& val ); 
// 以右值引用方法传递 val 参数 
template <class P>
  iterator insert ( const_iterator hint, P&& val );

hint 参数为迭代器,用于指定新键值对要添加到容器中的位置;val 参数指的是要添加容器中的键值对;添加成功返回的迭代器指向新添加的键值对,失败返回的迭代器指向容器中已有相同的键值对。注意最终存储的位置实际上不取决于 hint 参数(hash)

// 创建空 umap 容器 
unordered_map<string, string> umap; 
// 构建要添加的键值对 
std::pair<string, string>mypair("STL 教程", "http://c.biancheng.net/stl/"); 
// 创建接收 insert() 方法返回值的迭代器类型变量 
unordered_map<string, string>::iterator iter; 
// 调用第一种语法格式
iter = umap.insert(umap.begin(), mypair);

// 调用第二种语法格式 
iter = umap.insert(umap.begin(),std::make_pair("Python 教程", "http://c.biancheng.net/python/"));
将某个 unordered_map 容器指定区域所有键值对复制到另一个 unordered_map 容器中
template <class InputIterator> 
  void insert ( InputIterator first, InputIterator last );

其中 first 和 last 都为迭代器,[first, last)表示复制其它 unordered_map 容器中键值对的区域。

// 创建并初始化 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程","http://c.biancheng.net/stl/"}, 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"} }; 
// 创建一个空的 unordered_map 容器 
unordered_map<string, string> otherumap; 

// 指定要拷贝 umap 容器中键值对的范围 
unordered_map<string, string>::iterator first = ++umap.begin(); 
unordered_map<string, string>::iterator last = umap.end(); 
// 将指定 umap 容器中 [first,last) 区域内的键值对复制给 otherumap 容器
otherumap.insert(first, last);
一次想 unordered_map 容器添加多个键值对
void insert(initializer_list<value_type> il);

il 参数指可用于初始化列表的形式指定多个键值对元素

// 创建空的 umap 容器 
unordered_map<string, string> umap;
umap.insert({ {"STL 教程","http://c.biancheng.net/stl/"}, 
              {"Python 教程","http://c.biancheng.net/python/"},
              {"Java 教程","http://c.biancheng.net/java/"} });

emplace() 和 emplace_hint() 方法

template <class... Args> 
  pair<iterator, bool> emplace ( Args&&... args );

参数 args 表示可直接向该方法传递创建新键值对所需要的 2 个元素的值,其中第一个元素将作为键值对的键,另一个作为键值对的值。

当 emplace() 成功添加新键值对时,返回的迭代器指向新添加的键值对,bool 值为 True; 当 emplace()

添加新键值对失败时,说明容器中本就包含一个键相等的键值对,此时返回的迭代器指向的就是容器中键相同的这个键值对,bool 值为 False。

// 创建 umap 容器 
unordered_map<string, string> umap; 
// 定义一个接受 emplace() 方法的 pair 类型变量 
pair<unordered_map<string, string>::iterator, bool> ret; 
// 调用 emplace() 方法 
ret = umap.emplace("STL 教程", "http://c.biancheng.net/stl/");
emplace_hint() 方法的语法格式如下:

template <class... Args> 
  iterator emplace_hint ( const_iterator position, Args&&... args );

emplace_hint 不同之处:

emplace_hint() 方法的返回值仅是一个迭代器,而不再是 pair 类型变量。当该方法将新键值对成功添加到容器中时,返回的迭代器指向新添加的键值对;反之,如果添加失败,该迭代器指向的是容器中和要添加键值对键相同的那个键值对。 emplace_hint()方法还需要传递一个迭代器作为第一个参数,该迭代器表明将新键值对添加到容器中的位置。需要注意的是,新键值对添加到容器中的位置,并不是此迭代器说了算,最终仍取决于该键值对的键的值。该迭代器仅是建议。

// 创建 umap 容器 
unordered_map<string, string> umap; 
// 定义一个接受 emplace_hint() 方法的迭代器 
unordered_map<string,string>::iterator iter; 
// 调用 empalce_hint() 方法 
iter = umap.emplace_hint(umap.begin(),"STL 教程", "http://c.biancheng.net/stl/");

删除元素 erase()/clear()

erase():删除 unordered_map 容器中指定的键值对;
clear():删除 unordered_map 容器中所有的键值对,即清空容器。

erase()

接受一个正向迭代器,并删除该迭代器指向的键值对

// 创建 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
// 定义一个接收 erase() 方法的迭代器 
unordered_map<string,string>::iterator ret; 
// 删除容器中第一个键值对
ret = umap.erase(umap.begin());

返回一个指向被删除键值对之后位置的迭代器。

根据键值对的键来删除键值对

size_type erase ( const key_type& k );

k 表示目标键值对的键的值;返回一个整数,表示成功删除的键值对的数量

// 创建 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
int delNum = umap.erase("Python 教程");     // delNum 为 1  

删除指定范围内的所有键值对

unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
unordered_map<string, string>::iterator first = umap.begin(); 
unordered_map<string, string>::iterator last = --umap.end(); 
// 删除[fist,last)范围内的键值对
auto ret = umap.erase(first, last);   // 将仅剩最后一个键值对

clear()

一次性删除 unordered_map 容器中所有键值对

  unordered_map<string, string> umap{ 
      {"STL 教程", "http://c.biancheng.net/stl/"}, 
      {"Python 教程", "http://c.biancheng.net/python/"},
      {"Java 教程", "http://c.biancheng.net/java/"} };
  umap.clear();     // umap 容器清空,umap.size() = 0

你可能感兴趣的:(算法基础,哈希算法,散列表,算法)