关联容器详解(一)

本文为对《C++ primer (中文版第五版)》中有关关联容器的讲解和网上大神们的博客整理而成。

关联容器和顺序容器的根本不同在于:关联容器中的元素是按关键字来保存和访问的,而顺序容器中的元素则是按它们在容器中的位置来顺序保存和访问的。顺序容器有vector、deque、list、forward_list、array、string等,更多详情,可见melonstreet的博客。

关联容器支持高效的关键字查找访问。主要的关联容器类型是map和set,其中map中的元素是一些关键字-值(key-value)对:关键字起到索引的作用,值则表示索引相关联的数据;set中每个元素包括一个关键字,set支持高效的关键字查询操作-检查一个给定关键字是否在set中。关联容器类型如下:

一)关联容器的使用

  类似顺序容器,关联容器也是模板,所以为了定义一个map必须要指定关键字和值的类型,定义一个set同样要指定关键字的类型。

  1)map的使用--单词计数 

1 //统计每个单词在输入中出现的次数
2 map<string,size_t> word_count;
3 string word;
4
5 while(cin>>word)
6     ++word_count[word];

  关键字的类型是string,值的类型是int。进行下标操作是,我们使用string作为下标,得到与此string相关联的size_t类型的计数器。值得注意的是:如果word还未在map中,下标运算会创建一个新元素,其关键字是word,值为0,然后再将其值加1;从map中提取一个元素时,得到的是一个pair类的对象,pair为模板类型,保存两个名为first和second的公有数据成员,这里first成员保存关键字,second成员保存对应的值。

  2)set的使用--忽略某些单词

1 //忽略输入中的指定单词,给其他单词计数
2 set<string> exclude={"The","But","and","a"};
3 string word;
4
5 while(cin>>word)
6 {
7     if(exclude.find(word)==exclude.end())
8         ++word_count[word];
9 }

  对关联容器的初始化可以进行列表初始化,如,第一行。find调用返回一个迭代器,若,给定关键字在set中,迭代器指向该关键字,否则,find返回尾后迭代器。本程序中第7行,仅当单词不在exclude中时,才更新计数器。

二)关联容器概述

  关联容器不支持顺序容器的位置相关的操作,如push_back,因为,关联容器是根据关键词存储的。而且,关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。

  1)定义关联容器

  每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。i)可以将关联容器初始化为另一个同类型容器的拷贝;ii)从一个值范围来初始化关联容器;iii)对关联 容器进行值初始化。

  特别值得注意的是:初始化map时,必须提供关键字类型和值类型,我们将每个关键字-值对包围在花括号中:{key,value},来指出由它们一起构成map中的一个元素。如:

1 map<string,string> authors={
2     {"Joyce","James"},
3     {"Austen","Jane"}      };

authors初始化后,仅有两个元素。另一种初始化方法如下:

1 vector<int> ivec(10,1);
2 set<int> iset(ivec.begin(),ivec.end());

  2)关键字的要求

  对于有序的容器-map、multimap、set及multiset,关键字的类型必需定义元素比较的方法。i)可以向算法提供我们自己定义的比较操作,但是该操作必须在关键字类型上定义一个严格弱序 (看做“小于等于”);ii)使用关键字类型的比较函数。

三)关联容器操作

  

  针对map元素的类型:

1 map<string,int>::value_type v3;     //v3是一个pair<const string,int>

  1)当解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。值得注意的是:一个map的value_type是一个pair,可以改变pair的值,但是不可以改变关键字的关键字成员的值,同理set关键也是const的,可以使用一个set迭代器来读取元素,但不能修改。

1 auto map_it=word_count.begin();
2 map_it->first="new_key";    //错误:关键字不可修改

  遍历关联容器操作如下:

1 auto map_it=word_count.cbegin();
2 while(map_it !=word_count.cend())
3 {
4     ......
5 }   //也可以使用for循环

2)添加元素---map和set(以及对应的无序类型)包含不重复的关键字

  i)对set, insert有两个版本:接受一对迭代器;一个初始化列表。

1 vector<int> ivec={2,4,6,8,2,4,6,8};
2 set<int> set1;                          //注意无重复
3 set1.insert(ivec.begin(),ivec.end());   //set1中4个元素
4 set1.insert({1,3,5,7,1,3,5,7});         //set1中8个元素

  ii)对map有四种方式,最简单的是:在参数列表中使用花括号初始化。

word_count.insert({word,1});

  iii)检测insert的返回值

  对于不包含重复关键字的关联容器,insert返回的是一个pair,pair的first成员是一个指向给定关键字的元素的迭代器;second成员是一个bool型,若该关键字已在容器中,bool部分返回false,否则为true。

  iv)删除元素

  

  v)我们不能对一个multi-的关联容器进行下标操作,因为这些容器中可以有多个值和一个关键字向关联。注意区分,对map进行下标操作和解引用一个map迭代器的区别,前者返回一个mapped_type对应,后者返回一个value_type(即pair)。

  vi)访问元素

  若,我们所关心的只是一个特定的元素是否已在容器中,最好的选择是find,至于count在multi-容器中有更多的工作。

  值得注意的是:使用下标计数依赖这样一个特性:使用一个不存在的关键字作为下标,会插入一个新元素,其关键字为给定关键字,其值为0。有时候,若仅想知道给定关键字是否在map中,不想改变map,需使用find。

  针对multi-关联容器,可以使用:

  lower_bound和upper_bound来解决问题,这两个操作都是接受一个关键字,返回一个迭代器。若关键字在容器中,lower_bound返回的迭代器将指向第一个叫 具有该关键字的元素,而upper_bound返回的迭代器则指向最后一个匹配该关键字的元素之后的位置。若不在容器中,则两者返回相等的迭代器--指向一个不影响排序的关键字插入位置。

1 string dic="and";
2 for(auto beg=word_count.lower_bound(dic),end=word_count.upper_bound(dic);beg !=end;++beg)
3 {
4     ....
5 }

  使用equal_range函数。直接调用equal_range即可,该函数接受一个关键字,返回一个迭代器pair,若关键字存在,第一个迭代器指向第一个关键字匹配的元素,第二迭代器指向最后一个匹配元素之后的位置。

string dic="and";
for(auto pos=word_count.equal_range(dic);pos.first !=pos.second;++pos.first)
{
    ....
}

四)无序容器

  这些容器不是使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的==运算符。无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。 

时间: 2024-10-11 11:12:56

关联容器详解(一)的相关文章

Cocos2d-x3.0模版容器详解之——cocos2d::Vector&lt;T&gt;, cocos2d::Map&lt;K,V&gt;, cocos2d::Value

Cocos2d-x3.0模版容器详解之一:cocos2d::Vector<T>  http://www.cocoachina.com/bbs/read.php?tid=199793Cocos2d-x3.0模版容器详解之二:cocos2d::Map<K,V>  http://www.cocoachina.com/bbs/read.php?tid=199916Cocos2d-x3.0模版容器详解之三:cocos2d::Value  http://www.cocoachina.com/b

Cocos2d-x3.0模版容器详解之三:cocos2d::Value

1.概述 版本: v3.0 beta 语言: C++ 定义在 "COCOS2DX_ROOT/cocos/base" 路径下的 "CCValue.h" 的头文件中. ? 1 class Value; cocos2d::Valie 是一个包含了很多原生类型(int,float,double,bool,unsigned char,char* 和 std::string)外加 std::vector<Value>, std::unordered_map<s

STL 容器详解

STL的容器可以分为以下几个大类: 一:序列容器, 有vector, list, deque, string. 二 : 关联容器,     有set, multiset, map, mulmap, hash_set, hash_map, hash_multiset, hash_multimap 三: 其他的杂项: stack, queue, valarray, bitset 容器类共享部分公共接口.标准库定义的三种顺序容器类型:vector.list.deque(double-ended que

springIoc容器详解

闲聊 无论是做j2ee开发还是做j2se开发,spring都是一把大刀.当下流行的ssh三大框架中,spring是最不可替代的,如果不用hibernate和struts,我觉得都无关紧要,但是不能没有spring,可能有人说spring有啥用啊?直接new对象又有何妨,搞了个ioc这么麻烦,又难以理解,多了这么多配置,写代码时也没有感觉到它存在的价值,曾经我一直这么认为,就是带着这些疑问不断学习spring,渐渐了解它的价值.其实spring带来的不是某种持久化技术.mvc框架,缓存组件等等,它

c++Map容器 详解

ap是c++的一个标准容器,她提供了很好一对一的关系,在一些程序中建立一个map可以起到事半功倍的效果,总结了一些map基本简单实用的操作!1. map最基本的构造函数:   map<string , int >mapstring;         map<int ,string >mapint;   map<sring, char>mapstring;         map< char ,string>mapchar;   map<char ,in

搭建部署Docker容器详解实操

Docker 容器 :    容器是Docker又一核心的概念,简单来说,容器是独立运行的一个或一组应用,以及它们的运行态环境.对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用. 接下来具体介绍如何管理一个容器,包括创建.启动和停止等. 启动容器有两种方式:          (1.) 第一种是基于镜像新建一个容器并启动.   所需要的命令主要为docker run    实例:            -t:让docker分配一个为终端(pase

Spring框架:Spring容器详解

Spring容器 Spring容器可以帮助你管理所有的Bean对象,专业术语称之为IoC控制反转.在传统的程序中,对象的生成都是由开发者完成的.而在控制反转中,对象的生成全部都交给框架完成.这样的好处就是减少了程序的依赖性. Bean在Spring中的生命周期如下: 实例化.Spring通过new关键字将一个Bean进行实例化,JavaBean都有默认的构造函数,因此不需要提供构造参数. 填入属性.Spring根据xml文件中的配置通过调用Bean中的setXXX方法填入对应的属性. 事件通知.

Docker 容器详解

容器是 Docker 又一核心概念,简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境.对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用. 本章将具体介绍如何来管理一个容器,包括创建.启动和停止等. 启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动. 新建并启动 所需要的命令主要为docker run 下面的命令则启动一个 bash 终端,允许用户进行交互. -t 选项让 D

list容器详解

首先说说STL ( STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件.STL现在是C++的一部分,因此不用额外安装什么.它被内建在你的编译器之内.因为STL的list是一个简单的容器,所以我打算从它开始介绍STL如何使用.如果你懂得了这个概念,其他的就都没有问题了.另外,list容器是相当简单的,我们会看到这一点. STL容器可以保存对象,内建对象和类对象.它们会安全的保存对象,并定义我们能够操作的这个对象的接口.放在蛋架上的鸡蛋不会滚到桌上.它们很安全.因此,在STL容器中的对