C++ STL复习(12)无序容器

和关联式容器一样,无序容器也使用键值对(pair 类型)的方式存储数据。不过,它们有本质上的不同:

  • 关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构;
  • 无序容器的底层实现采用的是哈希表的存储结构。

基于底层实现采用了不同的数据结构,因此和关联式容器相比,无序容器具有以下 2 个特点:

  • 无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键;
  • 和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。

和关联式容器一样,无序容器只是一类容器的统称,其包含有 4 个具体容器,分别为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。

关联容器和无序容器仅有一个区别,关联容器会对存储的键值对进行排序,无序容器则不会。

这里举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建并初始化一个 unordered_map 容器,其存储的 <string,string> 类型的键值对
    std::unordered_map<std::string, std::string> my_uMap{
    
    
        {
    
    "C++","111"},
        {
    
    "Python","222"},
        {
    
    "Java","333"} };
    //查找指定键对应的值,效率比关联式容器高
    string str = my_uMap.at("C++");
    cout << "str = " << str << endl;
    //使用迭代器遍历哈希容器,效率不如关联式容器
    for (auto iter = my_uMap.begin(); iter != my_uMap.end(); ++iter)
    {
    
    
        //pair 类型键值对分为 2 部分
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

str = 111
Python 222
Java 333
C++ 111

从输出结果可以看出,无序容器没有对存储的数据进行排序。

1 unordered_map

unordered_map 容器在<unordered_map>头文件中,并位于 std 命名空间中。因此,如果想使用该容器,代码中应包含如下语句:

#include <unordered_map>
using namespace std;

1 创建 unordered_map容器的方法

常见的创建 unordered_map 容器的方法有以下几种。

1

通过调用 unordered_map 模板类的默认构造函数,可以创建空的 unordered_map 容器。比如:

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

2

当然,在创建 unordered_map 容器的同时,可以完成初始化操作。比如:

std::unordered_map<std::string, std::string> umap{
    
    
    {
    
    "Python","222"},
    {
    
    "Java","333"},
    {
    
    "C++","111"} };

3

另外,还可以调用 unordered_map 模板中提供的复制(拷贝)构造函数,将现有 unordered_map 容器中存储的键值对,复制给新建 unordered_map 容器。

例如,在第二种方式创建好 umap 容器的基础上,再创建并初始化一个 umap2 容器:

std::unordered_map<std::string, std::string> umap2(umap);

由此,umap2 容器中就包含有 umap 容器中所有的键值对。

除此之外,C++ 11 标准中还向 unordered_map 模板类增加了移动构造函数,即以右值引用的方式将临时 unordered_map 容器中存储的所有键值对,全部复制给新建容器。例如:

//返回临时 unordered_map 容器的函数
std::unordered_map <std::string, std::string > retUmap(){
    
    
    std::unordered_map<std::string, std::string>tempUmap{
    
    
        {
    
    "Python","222"},
    	{
    
    "Java","333"},
    	{
    
    "C++","111"} };
    return tempUmap;
}
//调用移动构造函数,创建 umap2 容器
std::unordered_map<std::string, std::string> umap2(retUmap());

注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。

4

当然,如果不想全部拷贝,可以使用 unordered_map 类模板提供的迭代器,在现有 unordered_map 容器中选择部分区域内的键值对,为新建 unordered_map 容器初始化。例如:

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

通过此方式创建的 umap2 容器,其内部就包含 umap 容器中除第 1 个键值对外的所有其它键值对。

2 unordered_map迭代器的用法

C++ STL 标准库中,unordered_map 容器迭代器的类型为前向迭代器(又称正向迭代器)。这意味着,假设 p 是一个前向迭代器,则其只能进行 *p、p++、++p 操作,且 2 个前向迭代器之间只能用 == 和 != 运算符做比较。

下面上个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "Python","222"},
        {
    
    "Java","333"},
        {
    
    "C++","111"} };
    
    cout << "umap 存储的键值对包括:" << endl;
    //遍历输出 umap 容器中所有的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << "<" << iter->first << ", " << iter->second << ">" << endl;
    }
    
    //获取指向指定键值对的前向迭代器
    unordered_map<string, string>::iterator iter = umap.find("Java");
    cout <<"umap.find(\"Java\") = " << "<" << iter->first << ", " << iter->second << ">" << endl;
    return 0;
}

输出结果:

umap 存储的键值对包括:
<C++, 111>
<Java, 333>
<Python, 222>
umap.find("Java") = <Java, 333>

需要注意的是,在操作 unordered_map 容器过程(尤其是向容器中添加新键值对)中,一旦当前容器的负载因子超过最大负载因子(默认值为 1.0),该容器就会适当增加桶的数量(通常是翻一倍),并自动执行 rehash() 成员方法,重新调整各个键值对的存储位置(此过程又称“重哈希”),此过程很可能导致之前创建的迭代器失效。

举个例子:

#include <iostream>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<int, int> umap;
    //向 umap 容器添加 50 个键值对
    for (int i = 1; i <= 50; i++) 
    {
    
    
        umap.emplace(i, i);
    }
    //获取键为 49 的键值对所在的范围
    auto pair = umap.equal_range(49);
    //输出 pair 范围内的每个键值对的键的值
    for (auto iter = pair.first; iter != pair.second; ++iter) 
    {
    
    
        cout << iter->first <<" ";
    }
    cout << endl;
    
    //手动调整最大负载因子数
    umap.max_load_factor(3.0);
    //手动调用 rehash() 函数重哈希
    umap.rehash(10);
    //重哈希之后,pair 的范围可能会发生变化
    for (auto iter = pair.first; iter != pair.second; ++iter) 
    {
    
    
        cout << iter->first << " ";
    }
    
    return 0;
}

输出结果:

49 
[1]    52795 segmentation fault (core dumped)  ./a

观察输出结果不难发现,之前用于表示键为 49 的键值对所在范围的 2 个迭代器,重哈希之后表示的范围发生了改变。

3 unordered_map元素获取

1

unordered_map 容器类模板中,实现了对 [ ] 运算符的重载,使得我们可以像“利用下标访问普通数组中元素”那样,通过目标键值对的键获取到该键对应的值。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "Python","222"},
        {
    
    "Java","333"},
        {
    
    "C++","111"} };
    //获取 "Java" 对应的值
    string str = umap["Java"];
    cout << str << endl;
    
    return 0;
}

输出结果:

333

需要注意的是,如果当前容器中并没有存储以 [ ] 运算符内指定的元素作为键的键值对,则此时 [ ] 运算符的功能将转变为:向当前容器中添加以目标元素为键的键值对。

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建空 umap 容器
    unordered_map<string, string> umap;
    //[] 运算符在 = 右侧
    string str = umap["STL"];
    //[] 运算符在 = 左侧
    umap["C++"] = "111";
   
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

C++ 111
STL 

可以看到,当使用 [ ] 运算符向 unordered_map 容器中添加键值对时,分为 2 种情况:

  • 当 [ ] 运算符位于赋值号(=)右侧时,则新添加键值对的键为 [ ] 运算符内的元素,其值为键值对要求的值类型的默认值(string类型默认值为空字符串);
  • 当 [ ] 运算符位于赋值号(=)左侧时,则新添加键值对的键为 [ ] 运算符内的元素,其值为赋值号右侧的元素。

2

unordered_map 类模板中,还提供有 at() 成员方法,和使用 [ ] 运算符一样,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出out_of_range异常。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "Python","222"},
        {
    
    "Java","333"},
        {
    
    "C++","111"} };
    //获取指定键对应的值
    string str = umap.at("Python");
    cout << str << endl;
    //执行此语句会抛出 out_of_range 异常
    cout << umap.at("GO教程");
    
    return 0;
}

输出结果:

222
terminate called after throwing an instance of 'std::out_of_range'
  what():  _Map_base::at
[1]    58306 abort (core dumped)  ./a

3

[ ] 运算符和 at() 成员方法基本能满足大多数场景的需要。除此之外,还可以借助 unordered_map 模板中提供的 find() 成员方法。
和前面方法不同的是,通过 find() 方法得到的是一个正向迭代器,该迭代器的指向分以下 2 种情况:

  • 当 find() 方法成功找到以指定元素作为键的键值对时,其返回的迭代器就指向该键值对;
  • 当 find() 方法查找失败时,其返回的迭代器和 end() 方法返回的迭代器一样,指向容器中最后一个键值对之后的位置。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "Python","222"},
        {
    
    "Java","333"},
        {
    
    "C++","111"} };
    //查找成功
    unordered_map<string, string>::iterator iter = umap.find("Python");
    cout << iter->first << " " << iter->second << endl;
    
    //查找失败
    unordered_map<string, string>::iterator iter2 = umap.find("GO");
    if (iter2 == umap.end()) 
    {
    
    
        cout << "当前容器中没有以\"GO\"为键的键值对";
    }
    
    return 0;
}

输出结果:

Python 222
当前容器中没有以"GO"为键的键值对%  

4

除了 find() 成员方法之外,甚至可以借助 begin()/end() 或者 cbegin()/cend(),通过遍历整个容器中的键值对来找到目标键值对。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "Python","222"},
        {
    
    "Java","333"},
        {
    
    "C++","111"} };
    //遍历整个容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        //判断当前的键值对是否就是要找的
        if (!iter->first.compare("Java")) 
        {
    
    
            cout << iter->second << endl;
            break;
        }
    }
    
    return 0;
}

输出结果:

333

4 unordered_map insert()用法

1

insert() 方法可以将 pair 类型的键值对元素添加到 unordered_map 容器中,其语法格式有 2 种:

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

以上 2 种格式中,参数 val 表示要添加到容器中的目标键值对元素;该方法的返回值为 pair类型值,内部包含一个 iterator 迭代器和 bool 变量:

  • 当 insert() 将 val 成功添加到容器中时,返回的迭代器指向新添加的键值对,bool 值为 True;
  • 当 insert()添加键值对失败时,意味着当前容器中本就存储有和要添加键值对的键相等的键值对,这种情况下,返回的迭代器将指向这个导致插入操作失败的迭代器,bool值为 False。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建空 umap 容器
    unordered_map<string, string> umap;
    //构建要添加的键值对
    std::pair<string, string> mypair("STL", "222");
    //创建接收 insert() 方法返回值的pair类型变量
    std::pair<unordered_map<string, string>::iterator, bool> ret;
    //调用 insert() 方法的第一种语法格式
    ret = umap.insert(mypair);
    cout << "bool = " << ret.second << endl;
    cout << "iter -> " << ret.first->first <<" " << ret.first->second << endl;
   
    //调用 insert() 方法的第二种语法格式
    ret = umap.insert(std::make_pair("Python","333"));
    cout << "bool = " << ret.second << endl;
    cout << "iter -> " << ret.first->first << " " << ret.first->second << endl;
    return 0;
}

输出结果:

bool = 1
iter -> STL 222
bool = 1
iter -> Python 333

2

除此之外,insert() 方法还可以指定新键值对要添加到容器中的位置,其语法格式如下:

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

以上 2 种语法格式中,hint 参数为迭代器,用于指定新键值对要添加到容器中的位置;val 参数指的是要添加容器中的键值对;方法的返回值为迭代器:

  • 如果 insert() 方法成功添加键值对,该迭代器指向新添加的键值对;
  • 如果 insert() 方法添加键值对失败,则表示容器中本就包含有相同键的键值对,该方法返回的迭代器就指向容器中键相同的键值对;

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建空 umap 容器
    unordered_map<string, string> umap;
    //构建要添加的键值对
    std::pair<string, string> mypair("STL", "333");
    //创建接收 insert() 方法返回值的迭代器类型变量
    unordered_map<string, string>::iterator iter;
    //调用第一种语法格式
    iter = umap.insert(umap.begin(), mypair);
    cout << "iter -> " << iter->first <<" " << iter->second << endl;
   
    //调用第二种语法格式
    iter = umap.insert(umap.begin(),std::make_pair("Python", "222"));
    cout << "iter -> " << iter->first << " " << iter->second << endl;
    
    return 0;
}

输出结果:

iter -> STL 333
iter -> Python 222

3

insert() 方法还支持将某一个 unordered_map 容器中指定区域内的所有键值对,复制到另一个 unordered_map 容器中,其语法格式如下:

template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );

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

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建并初始化 umap 容器
    unordered_map<string, string> umap{
    
     {
    
    "STL","111"}, {
    
    "Python","333"}, {
    
    "Java","222"} };
    //创建一个空的 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);
    //遍历 otherumap 容器中存储的键值对
    for (auto iter = otherumap.begin(); iter != otherumap.end(); ++iter)
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

STL 111
Python 333

4

除了以上 3 种方式,insert() 方法还支持一次向 unordered_map 容器添加多个键值对,其语法格式如下:

void insert ( initializer_list<value_type> il );

其中,il 参数指的是可以用初始化列表的形式指定多个键值对元素。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建空的 umap 容器
    unordered_map<string, string> umap;
    //向 umap 容器同时添加多个键值对
    umap.insert( {
    
    {
    
    "STL","111"},
    		   {
    
    "Python","333"},
    		   {
    
    "Java","222"} } );
    //遍历输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter)
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

Java 222
Python 333
STL 111

5 unordered_map emplace()和emplace_hint()

向已有 unordered_map 容器中添加新键值对,可以通过调用 insert() 方法,但其实还有更好的方法,即使用 emplace() 或者 emplace_hint() 方法,它们完成“向容器中添加新键值对”的效率,要比 insert() 方法高。

1

emplace() 方法的用法很简单,其语法格式如下:

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

其中,参数 args 表示可直接向该方法传递创建新键值对所需要的 2 个元素的值,其中第一个元素将作为键值对的键,另一个作为键值对的值。也就是说,该方法无需我们手动创建键值对,其内部会自行完成此工作。

另外需要注意的是,该方法的返回值为 pair 类型值,其包含一个迭代器和一个 bool 类型值:

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

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap;
    //定义一个接受 emplace() 方法的 pair 类型变量
    pair<unordered_map<string, string>::iterator, bool> ret;
    //调用 emplace() 方法
    ret = umap.emplace("STL", "222");
    //输出 ret 中包含的 2 个元素的值
    cout << "bool = " << ret.second << endl;
    cout << "iter -> " << ret.first->first << " " << ret.first->second << endl;
    
    return 0;
}

输出结果:

bool = 1
iter -> STL 222

2

emplace_hint() 方法的语法格式如下:

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

和 emplace() 方法相同,emplace_hint() 方法内部会自行构造新键值对,因此我们只需向其传递构建该键值对所需的 2 个元素(第一个作为键,另一个作为值)即可。不同之处在于:

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

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap;
    //定义一个接受 emplace_hint() 方法的迭代器
    unordered_map<string,string>::iterator iter;
    //调用 empalce_hint() 方法
    iter = umap.emplace_hint(umap.begin(), "STL", "222");
    //输出 emplace_hint() 返回迭代器 iter 指向的键值对的内容
    cout << "iter -> " << iter->first << " " << iter->second << endl;
    
    return 0;
}

输出结果:

iter -> STL 222

6 unordered_map删除元素:erase()和clear()

为了满足不同场景删除 unordered_map 容器中键值对的需要,此容器的类模板中提供了 3 种语法格式的 erase() 方法。

1

erase() 方法可以接受一个正向迭代器,并删除该迭代器指向的键值对。该方法的语法格式如下:

iterator erase ( const_iterator position );

其中 position 为指向容器中某个键值对的迭代器,该方法会返回一个指向被删除键值对之后位置的迭代器。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "STL", "111"},
        {
    
    "Python", "222"},
        {
    
    "Java", "333"} };
        
    //输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    cout << "erase:" << endl;
    //定义一个接收 erase() 方法的迭代器
    unordered_map<string,string>::iterator ret;
    //删除容器中第一个键值对
    ret = umap.erase(umap.begin());
    //输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    cout << "ret = " << ret->first << " " << ret->second << endl;
    
    return 0;
}

输出结果:

Java 333
Python 222
STL 111
erase:
Python 222
STL 111
ret = Python 222

2

我们还可以直接将要删除键值对的键作为参数直接传给 erase() 方法,该方法会自行去 unordered_map 容器中找和给定键相同的键值对,将其删除。erase() 方法的语法格式如下:

size_type erase ( const key_type& k );

其中,k 表示目标键值对的键的值;该方法会返回一个整数,其表示成功删除的键值对的数量。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "STL", "111"},
        {
    
    "Python", "222"},
        {
    
    "Java", "333"} };
    //输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    int delNum = umap.erase("Python");
    cout << "delNum = " << delNum << endl;
    //再次输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

输出结果:

Java 333
Python 222
STL 111
delNum = 1
Java 333
STL 111

3

除了支持删除 unordered_map 容器中指定的某个键值对,erase() 方法还支持一次删除指定范围内的所有键值对,其语法格式如下:

iterator erase ( const_iterator first, const_iterator last );

其中 first 和 last 都是正向迭代器,[first, last) 范围内的所有键值对都会被 erase() 方法删除;同时,该方法会返回一个指向被删除的最后一个键值对之后一个位置的迭代器。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "STL", "111"},
        {
    
    "Python", "222"},
        {
    
    "Java", "333"} };
    //first 指向第一个键值对
    unordered_map<string, string>::iterator first = umap.begin();
    //last 指向倒数第二个键值对
    unordered_map<string, string>::iterator last = umap.begin()++;

    //删除[fist,last)范围内的键值对
    auto ret = umap.erase(first, last);
    
    //输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    cout << "ret -> " << ret->first << " " << ret->second << endl;
    return 0;
}

输出结果:

Java 333
Python 222
STL 111
ret -> Java 333

4

在个别场景中,可能需要一次性删除 unordered_map 容器中存储的所有键值对,可以使用 clear() 方法,其语法格式如下:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建 umap 容器
    unordered_map<string, string> umap{
    
    
        {
    
    "STL", "111"},
        {
    
    "Python", "222"},
        {
    
    "Java", "333"} };
    //输出 umap 容器中存储的键值对
    for (auto iter = umap.begin(); iter != umap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    //删除容器内所有键值对
    umap.clear();
    cout << "umap size = " << umap.size() << endl;
    
    return 0;
}

输出结果:

Java 333
Python 222
STL 111
umap size = 0

2 unordered_multimap

和 unordered_map 容器一样,unordered_multimap 容器也以键值对的形式存储数据,且底层也采用哈希表结构存储各个键值对。两者唯一的不同之处在unordered_multimap 容器可以存储多个键相等的键值对,而 unordered_map 容器不行。

STL 标准库中实现 unordered_multimap 容器的模板类并没有定义在以自己名称命名的头文件中,而是和 unordered_map 容器一样,定义在<unordered_map>头文件,且位于 std 命名空间中。因此,在使用 unordered_multimap 容器之前,程序中应包含如下 2 行代码:

#include <unordered_map>
using namespace std;

1 创建C++ unordered_multimap容器的方法

1

利用 unordered_multimap 容器类模板中的默认构造函数,可以创建空的 unordered_multimap 容器。比如:

std::unordered_multimap<std::string, std::string> myummap;

2

当然,在创建空 unordered_multimap 容器的基础上,可以完成初始化操作。比如:

unordered_multimap<string, string> myummap{
    
    
    {
    
    "Python","222"},
    {
    
    "Java","333"},
    {
    
    "Linux","111"} };

3

另外,unordered_multimap 模板中还提供有复制(拷贝)构造函数,可以实现在创建 unordered_multimap 容器的基础上,用另一 unordered_multimap 容器中的键值对为其初始化。
例如,在第二种方式创建好 myummap 容器的基础上,再创建并初始化一个 myummap2 容器:

unordered_multimap<string, string> myummap2(myummap);

由此,刚刚创建好的 myummap2 容器中,就包含有 myummap 容器中所有的键值对。

除此之外,C++ 11 标准中还向 unordered_multimap 模板类增加了移动构造函数,即以右值引用的方式将临时 unordered_multimap 容器中存储的所有键值对,全部复制给新建容器。例如:

//返回临时 unordered_multimap 容器的函数
std::unordered_multimap <std::string, std::string > retUmmap() {
    
    
    std::unordered_multimap<std::string, std::string>tempummap{
    
    
    			{
    
    "Python","222"},
    			{
    
    "Java","333"},
    			{
    
    "Linux","111"} };
    return tempummap;
}
//创建并初始化 myummap 容器
std::unordered_multimap<std::string, std::string> myummap(retummap());

注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。

4

当然,如果不想全部拷贝,可以使用 unordered_multimap 类模板提供的迭代器,在现有 unordered_multimap 容器中选择部分区域内的键值对,为新建 unordered_multimap 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_multimap<std::string, std::string> myummap2(++myummap.begin(), myummap.end());

通过此方式创建的 myummap2 容器,其内部就包含 myummap 容器中除第 1 个键值对外的所有其它键值对。

举个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main()
{
    
    
    //创建空容器
    std::unordered_multimap<std::string, std::string> myummap;
    //向空容器中连续添加 5 个键值对
    myummap.emplace("Python", "222");
    myummap.emplace("STL", "555");
    myummap.emplace("Java", "333");
    myummap.emplace("C++", "111");
    myummap.emplace("C++", "444");
    //输出 muummap 容器存储键值对的个数
    cout << "myummmap size = " << myummap.size() << endl;
    //利用迭代器输出容器中存储的所有键值对
    for (auto iter = myummap.begin(); iter != myummap.end(); ++iter) 
    {
    
    
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

myummmap size = 5
C++ 444
C++ 111
Java 333
STL 555
Python 222

3 unordered_set容器

实现 unordered_set 容器的模板类定义在<unordered_set>头文件,并位于 std 命名空间中。这意味着,如果程序中需要使用该类型容器,则首先应该包含如下代码:

#include <unordered_set>
using namespace std;

1 创建C++ unordered_set容器

1

通过调用 unordered_set 模板类的默认构造函数,可以创建空的 unordered_set 容器。比如:

std::unordered_set<std::string> uset;

2

当然,在创建 unordered_set 容器的同时,可以完成初始化操作。比如:

std::unordered_set<std::string> uset{
    
     "C++",
                                      "Java",
                                      "Linux" };

通过此方法创建的 uset 容器中,就包含有 3 个 string 类型元素。

3

还可以调用 unordered_set 模板中提供的复制(拷贝)构造函数,将现有 unordered_set 容器中存储的元素全部用于为新建 unordered_set 容器初始化。
例如,在第二种方式创建好 uset 容器的基础上,再创建并初始化一个 uset2 容器:

std::unordered_set<std::string> uset2(uset);

由此,umap2 容器中就包含有 umap 容器中所有的元素。

除此之外,C++ 11 标准中还向 unordered_set 模板类增加了移动构造函数,即以右值引用的方式,利用临时 unordered_set 容器中存储的所有元素,给新建容器初始化。例如:

//返回临时 unordered_set 容器的函数
std::unordered_set <std::string> retuset() {
    
    
    std::unordered_set<std::string> tempuset{
    
     "http://c.biancheng.net/c/",
                                              "http://c.biancheng.net/java/",
                                              "http://c.biancheng.net/linux/" };
    return tempuset;
}
//调用移动构造函数,创建 uset 容器
std::unordered_set<std::string> uset(retuset());

4

当然,如果不想全部拷贝,可以使用 unordered_set 类模板提供的迭代器,在现有 unordered_set 容器中选择部分区域内的元素,为新建 unordered_set 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_set<std::string> uset2(++uset.begin(),uset.end());

对于其他的成员函数,下面举个例子:

#include <iostream>
#include <string>
#include <unordered_set>

using namespace std;

int main()
{
    
    
    //创建一个空的unordered_set容器
    std::unordered_set<std::string> uset;
    //给 uset 容器添加数据
    uset.emplace("Java");
    uset.emplace("C++");
    uset.emplace("Python");
    //查看当前 uset 容器存储元素的个数
    cout << "uset size = " << uset.size() << endl;
    //遍历输出 uset 容器存储的所有元素
    for (auto iter = uset.begin(); iter != uset.end(); ++iter) 
    {
    
    
        cout << *iter << endl;
    }
    return 0;
}

输出结果:

uset size = 3
Python
C++
Java

4 unordered_multiset容器

实现 unordered_multiset 容器的模板类并没有定义在以该容器名命名的文件中,而是和 unordered_set 容器共用同一个<unordered_set>头文件,并且也位于 std 命名空间。因此,如果程序中需要使用该类型容器,应包含如下代码:

#include <unordered_set>
using namespace std;

1 创建C++ unordered_multiset容器

1

调用 unordered_multiset 模板类的默认构造函数,可以创建空的 unordered_multiset 容器。比如:

std::unordered_multiset<std::string> umset;

2

当然,在创建 unordered_multiset 容器的同时,可以进行初始化操作。比如:

std::unordered_multiset<std::string> umset{
    
     "C++",
                                            "Java",
                                            "Linux" };

3

还可以调用 unordered_multiset 模板中提供的复制(拷贝)构造函数,将现有 unordered_multiset 容器中存储的元素全部用于为新建 unordered_multiset 容器初始化。
例如,在第二种方式创建好 umset 容器的基础上,再创建并初始化一个 umset2 容器:

std::unordered_multiset<std::string> umset2(umset);

由此,umap2 容器中就包含有 umap 容器中所有的元素。

除此之外,C++ 11 标准中还向 unordered_multiset 模板类增加了移动构造函数,即以右值引用的方式,利用临时 unordered_multiset 容器中存储的所有元素,给新建容器初始化。例如:

//返回临时 unordered_multiset 容器的函数
std::unordered_multiset <std::string> retumset() {
    
    
    std::unordered_multiset<std::string> tempumset{
    
     "C++",
                                            		"Java",
                                            		"Linux" };
    return tempumset;
}
//调用移动构造函数,创建 umset 容器
std::unordered_multiset<std::string> umset(retumset());

4

当然,如果不想全部拷贝,可以使用 unordered_multiset 类模板提供的迭代器,在现有 unordered_multiset 容器中选择部分区域内的元素,为新建 unordered_multiset 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_multiset<std::string> umset2(++umset.begin(), umset.end());

举个例子,展示一下其成员函数:

#include <iostream>
#include <string>
#include <unordered_set>

using namespace std;

int main()
{
    
    
    //创建一个空的unordered_multiset容器
    std::unordered_multiset<std::string> umset;
    //给 uset 容器添加数据
    umset.emplace("Java");
    umset.emplace("C++");
    umset.emplace("Python");
    umset.emplace("C++");
    //查看当前 umset 容器存储元素的个数
    cout << "umset size = " << umset.size() << endl;
    //遍历输出 umset 容器存储的所有元素
    for (auto iter = umset.begin(); iter != umset.end(); ++iter) 
    {
    
    
        cout << *iter << endl;
    }
    return 0;
}

输出结果:

umset size = 4
Python
C++
C++
Java

猜你喜欢

转载自blog.csdn.net/qq_24649627/article/details/108037118
今日推荐