C++学习笔记(二)联想容器
概述
C++primer对联想容器的定义如下:
A type that holds a collection of objects that supports efficient lookup by key
实际上,和序列式容器不同,联想容器是考关键字进行搜索的,它的底层实现不是简单的顺序表或链表,而是更加复杂的红黑树,这种数据结构具备顺序表和链表的双重优点,同时拥有很高插入效率和查询效率。并且,与序列式容器不同的还有一点,就是无论是插入,还是删除操作,都不会使以前已经持有的迭代器失效(当然,指向被删除元素的迭代器除外),这主要跟它底层的数据结构的实现有关。
STL支持的联想容器有map,set 以及 multimap,multiset。其中,map维护的是有KEY和VALUE组成的键值对,而set则只存储键。map和set都要求键必须唯一,而 multimap和multiset则是它们的键可以不唯一的版本。
实际上,在unix和linux平台下,都有一个库叫isc,它是C语言的一个包装库。许多人认为直接使用这些函数会比STL map速度快。其实不然,它们的区别不再于算法,而在于内存碎片。如果直接使用这些函数,你需要自己去new一些节点,当节点特别多, 而且进行频繁的删除和插入的时候,内存碎片就会存在,而STL采用自己的Allocator分配内存,以内存池的方式来管理这些内存,会大大减少内存碎 片,从而会提升系统的整体性能。有位叫Winter的前辈在自己的系统中做过测试,他把以前所有直接用isc函数的代码替换成map,程序速度基本一致。当时间运行很长时间后(例如后台服务程序),map的优势就会体现出来。从另外一个方面讲,使用map会大大降低你的编码难度,同时增加程序的可读性。何乐而不为?
map
前面说过,map里存储的实际上是键值对pair,这是一个定义在utility头文件里的数据结构,它有两个公共域,first和second。map是容器,而所有的容器都是可以用迭代器去访问的,需要注意的是,迭代出来的值是pair而不是value。除了通过迭代器去访问外,map还可以使用下标运算符,即把key当做索引,从而得到value值。例如:
[cpp]
map<string, int> my_map;
my_map["hello"] = 3;
需要注意的是,使用下标运算符去索引value,当键不存在时,它会利用这个键在这个map中插入一个新的键值对,对于value,所使用的是其默认构造函数。这在某些情况下会造成我们不希望看见的副作用,所以,STL同样为我们提供了另外一种索引方式,即find函数,它返回的是与给定键关联的pair的迭代器引用,当key不存在时,则返回off-the-end
iterator。如果只是想知道map中是否已经插入了指定的KEY,也可以使用另外一个函数count,它返回的是与指定key对应的pair的数量,对于map,它的返回值只有1和0两种情况,这个函数在multimap中可能更有用。
对于插入操作,可以直接使用下标运算符,但那样会照成对于新值的重复初始化,更有效的方式实际上是使用insert函数,与下标运算符不同的是,无论使用哪个版本的insert,如果该键已存在,则会添加失败,而前者则能成功插入,并将旧值替换为新值。
set
对于set,它的使用方式基本和map相同,由于它的数据结构里面只存有键,因此,它不支持下标运算符。set在某些方向和序列式容器有些相似,除了操作的效率不同外,它们之间最大的区别就是由于set要求KEY必须唯一,所以它不会重复的进行插入,如果利用一对vector的迭代器来初始化set,那么里面重复的值将被忽略。例如:
[cpp]
// define a vector with 20 elements, holding two copies of each number from 0 to 9
vector<int> ivec;
for (vector<int>::size_type i = 0; i != 10; ++i) {
ivec.push_back(i);
ivec.push_back(i); // duplicate copies of each number
}
// iset holds unique elements from ivec
set<int> iset(ivec.begin(), ivec.end());
cout << ivec.size() << endl; // prints 20
cout << iset.size() << endl; // prints 10
multimap和multiset
multimap和multiset实际上就是map和set的允许键重复的版本,它们的操作方式和它们的普通版本基本一致。当然,由于他们允许键重复,在一些操作上自然会有所区别。例如,对于multimap,它不能像map那样使用下标运算符,因为它保存的不是一个单独的值。
因为键值不要求唯一,所以对于multimap和multiset,insert函数总能成功的执行,并在相应键下添加上一个新的value(multiset只添加键)。需要注意的是,multimap的设计和map<key, vector>的结构并不类似,multimap只是简单的允许重复键而已,也就是说,如果与某个key有三个关联的value,那么将会有三个pair存在,只是由于红黑树的有序性,它们的迭代序列是连续的而已。
作者:jus易做图anda
补充:软件开发 , C++ ,