1.关联容器与顺序容器的对比
1)关联容器是通过键存储和读取元素,而顺序容器则是通过元素在容器中的位置顺序存储和访问元素
2)物理存储方式:一般关联容器是非连续内存存储,而顺序容器一部分是连续存储在内存中,随机访问速度较快,如vector、deque
关联容器一般包括map、set两种基本的关联容器:
【map】关联数组,元素通过键来存储和读取;适用于需要存储/修改每个键所关联的值的情况
【set】大小可变的集合,支持通过键实现的快速读取;希望有效地存储不同值的集合
map和set不允许为同一个键添加第二个元素,但是还有支持一个键对应多个实例的multimap和multiset类型:
【multimap】支持同一个键多次出现的map类型
【multiset】支持同一个键多次出现的set类型
2.pair类型
pair类型的操作
pair<T1, T2>p1; //创建一个空的pair对象,它的两个元素类型分别为T1、T2,采用值初始化
pair<T1, T2>p1(v1, v2); //创建一个pair对象,first成员被初始化为v1,second成员被初始化被v2
make_pair(v1,v2); //v1,v2创建一个新的pair对象,其元素类型分别是v1,v2的类型
p1 < p2; //两个pair对象之间的小于运算,其定义遵循字典次序
p1 == p2; //如果两个pair对象的first和second成员依次相等,则两个对象相等
p.first; //p中的first的公有数据成员
p.second; //p中的second的公有数据成员
1)pair的创建和初始化,与顺序容器类似,如下:
pair<string, string> anon;//调用默认构造函数来初始化
pair<string, int> word_count;
pair<string, vector<int>> line;
pair<string, string> author("jim", "weshon"); //定义时提供初始化
typedef pair<string, string> Author;
Author product("marcel", "Product");
2)pair对象的操作,支持== 、< ,first、second成员的访问,如下
string firstbook;
if (author.first == "jim" && author.second == "weshon")
{
firstbook = "stephon hero"; //
}
3)生成新的pair对象,如下:
pair<string, string> next_author;
string first, second;
while (cin >> first >> second)
{
next_author = make_pair(first, second);
//pair<string, string> next_author(first, second); //与上面创建类似
}
3. map容器
map的构造函数:
map<k,v>; //创建一个名为m的空map对象,其键和值的类型分别为k和v
map<k,v>m(m2); //创建m2的副本m,m与m2必须具有相同的键类型和值类型
map<k,v>m(b,e); //创建map类型的对象m,存储迭代器b和e标记的范围内的所有元素的副本,元素的类型必须能转换为pair<const k, v>
map中键类型的约束,键必须支持“<”操作符,键不允许修改
map<k,v>::key_type //索引的键的类型
map<k,v>::mapped_type //键所关联的值的类型
map<k,v>::value_type //pair类型,first成员具有map<const k, v>::key_type类型,second成员具有map<const k, v>::mapped_type类型
1)map对象的定义,支持如下几种:
map<string, int> word_count;
map<string, list<size_t>> line_count;
map<vector<int>::iterator, int> vcmap;
//map<list<int>::iterator, int> lsmap; //error, list can't support < operator
以上为map<key, value> m; 的构函类型
注意:键一定要支持"<"操作符, 如上list就不支持操作符<
2)map迭代器的使用,如下
map<string, int>::iterator map_iter = word_count.begin(); //iter: pair<const string, int>
cout << map_iter->first << " " << map_iter->second << endl;
//map_iter->first = "new key"; // error key is const, can't copy
map_iter->second ++;
map的迭代器键类型为const,如上键类型为pair<const string, int>
3)下标访问元素对象,如果不存在,就会创建一个新元素
word_count["jim"] = 1;
word_count["sun"] = 1;
由于在这两个之前,没有插入任何元素,所以这两个操作都会插入,如下正确的使用迭代器才是必须的:
cout << "inset map data: key value"<< endl;
string word;
while (cin >> word)
{
if (word == "@") break;
++word_count[word];
}
map_iter = word_count.begin();
for ( ; map_iter != word_count.end(); ++map_iter)
{
cout << map_iter->first << " " << map_iter->second << endl;
}
将重复出现的键的值+1,新出现的键直接插入,并将值置为1,可以计算相同元素出现的次数
4)map插入元素
m.insert(e); //e是m上的value_type类型,如果键不在m中,则插入新元素,在的话,保持m不变;返回值为一个pair类型的对象,包含指向键位e.first的元素的map迭代器以及一个bool类型的对象,表示是否插入了该元素
m.insert(beg,end); //beg和end标记元素范围的迭代器,元素类型必须为m.value_type,返回值为void
m.insert(iter,e); //e是一个用在m上的value_type类型的值,不在插入以iter为起点搜索新元素的位置;返回值为一个迭代器指向m中具有给定元素键的元素
string word = "";
while (cin >> word)
{
if (word == "@")
break;
//word_count.insert(map<string, int>::value_type("Anna", 1));
//word_count.insert(make_pair("Jim", 1));
//typedef map<string, int>::value_type valType;
//word_count.insert(valType("COM", 1));
pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
if (!ret.second)
{
++ret.first->second;
}
//++word_count[word];
}
(1)使用insert可以避免下标操作所带来的副作用,不必要的初始化
(2)检测返回值,带有一个简直pair形参的inset版本将返回一个值:包含一个迭代器和一个bool值的pair类型;其中迭代器指向map中具有相应键的元素,而bool表示是否插入了该元素。如果存在不插入,返回false,不存在则插入,返回true,但是迭代器都将指向具有给定键的元素。
5)查找并读取元素
前面已经讲过下标操作能访问和读取元素,但是副作用很严重。map容器提供了两个操作:count和find
m.count(k);返回m中k的出现次数
m.find(k);如果m容器中存在按k索引的元素,则返回指向该元素的迭代器;不存在的话,返回超出末端的迭代器
if (word_count.count("jim"))
{
cout << "jim = " << word_count["jim"] << endl;
}
map_iter = word_count.find("tom");
if (map_iter != word_count.end())
{
word_count["tom"] = 250;
cout << "tom = " << map_iter->second << endl;
}
6)删除元素
m.erase(k); //删除m中关键字为k的元素;返回size_type类型的值,表示删除的元素个数
m.erase(p); //删除m中迭代器p指向的元素;p必须存在,且不等于m.end();返回void类型
m.erase(b,e); //删除一段范围内的元素,由迭代器b和e标记;返回为void类型
word_count.erase("jim");
map返回值只可能是0或者1,返回0表示不存在
7)map对象的迭代遍历
map<string, int>::iterator map_iter = word_count.begin(); //iter: pair<const string, int> 上面已有
map_iter = word_count.begin();
for ( ; map_iter != word_count.end(); ++map_iter)
{
cout << map_iter->first << " " << map_iter->second << endl;
}