C++(15)STL实践与分析之Map类型

来源:互联网 发布:js跳转页面弹出新页面 编辑:程序博客网 时间:2024/05/29 03:19

STL实践与分析

--map类型(上)

引:

    map是键-值对的集合。map类型通常可以理解为关联数组:可以通过使用键作为下标来获取一个值,正如内置数组类型一样;而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在容器中的位置来获取。


一、map对象的定义

1、定义map对象时,必须分别指明键和值的类型:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. map<string,int> wordCnt;  


map的构造函数

map<K,V>m;

创建一个名为m空对象,其键和值的类型分别为KV

map<K,V>m(m2);

创建m2的对象副本mmm2的键与值的类型必须完全一致

map<K,V>m(b,e);

创建map类型的对象m,存储迭代器be标记的范围内所有元素的副本。

迭代器元素的类型必须能转换为pair<constK, V>


2、键类型的约束

    在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的<操作符来实现键(keytype)的比较。

    对于键类型,唯一的约束就是必须支持<操作符,至于是否支持其他的关系或相等运算,则不作要求。

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //P310 习题10.5  
  2.     typedef list<int> lineNo;  
  3.     map<string,lineNo> word_line;  
  4.     //等同于  
  5.     map<string,list<int> > word_line;  

二、map定义的类型

由于map对象的元素是键-值对,因此map对象的value_type是存储元素的键以及值类型的pair类型,而且键为const

map类定义的类型

map<K,V>::key_type

map容器中,用作索引的键类型

map<K,V>::mapped_type

map容器中,键所关联的值类型

map<K,V>::value_type

一个pair类型:first元素具有

constmap<K,V>::key_type类型,而second元素则为map<K,V>::mapped_type类型


1、对map迭代器进行解引用将产生pair类型的对象

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. map<string,int> word_count;  
  2. word_count.insert(make_pair("C++ Primer",1));  
  3. map<string,int>::iterator map_iter = word_count.begin();  
  4. cout << map_iter -> first << " "  
  5.      << map_iter -> second << endl;  
  6. map_iter -> first = "Effective C++";    //Error  
  7. map_iter -> second = 3;                 //OK  

2map容器额外定义的类型别名

    map类额外定义了两种类型:key_typemapped_type,以获得键或值的类型。对于word_count,key_typestring类型(注意不是conststring,mapped_type则是int型。

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. map<string,int> word_count;  
  2. word_count.insert(make_pair("C++ Primer",1));  
  3. map<string,int>::iterator map_iter = word_count.begin();  
  4.   
  5. map<string,int>::key_type first = map_iter -> first;  
  6. map<string,int>::mapped_type second = map_iter -> second;  
  7.   
  8. first = "Effective C++";  
  9.   
  10. cout << first << " "  
  11.      << second << endl;  

三、给map添加元素

添加元素:

    1、可以使用insert成员实现;

    2、或者先通过下标操作符获取元素,然后给获取的元素赋值。


四、使用下标访问map对象

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 该程序将发生下面几件事  
  2.  *1. 在 word_count 中查找键为 Anna 的元素,没有找到。  
  3.  *2. 将一个新的键-值对插入到 word_count 中。它的键是 const string 类型的对象,  
  4.  *  保存 Anna。而它的值则采用值初始化,这就意味着在本例中值为 0。  
  5.  *3. 将这个新的键-值对插入到 word_count 中。  
  6.  *4. 读取新插入的元素,并将它的值赋为 1。  
  7.  */  
  8. map<string,int> word_count;  
  9. word_count["Anna"] = 1;  
  10. cout << word_count["Anna"] << endl;  
  11. cout << word_count["anna"] << endl;  
  12. cout << word_count.size() << endl;  

使用下标访问map与使用下标访问数组或vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新元素,它的键即为该下标值

1、下表操作符返回值的使用

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //通常来说,下标操作符返回左值  
  2. cout << word_count["Anna"] << endl;  
  3. ++ word_count["Anna"];  
  4. cout << word_count["Anna"] << endl;  

有别于vectorstring类型,map下标操作符返回的类型与对map迭代器进行解引用或的类型不同:map迭代器返回的是value_type类型的值– 包含constkey_typemapped_type类型的pair对象;而下标操作符返回一个mapped_type类型的值。


2、使用下标行为的意义

    对于map容器,如果下标所表示的键在容器中不存在,则添加新元素,这一特性可使程序惊人地简练:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. map<string,int> word_cnt;  
  2. string str;  
  3.   
  4. while (cin >> str)  
  5. {  
  6.     ++ word_cnt[str];  
  7. }  
  8.   
  9. for (map<string,int>::iterator iter = word_cnt.begin(); iter != word_cnt.end(); ++iter)  
  10. {  
  11.     cout << "First: " << iter -> first << "\tSecond: " << iter -> second << endl;  
  12. }  

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //P312 习题10.9  
  2. int main()  
  3. {  
  4.     freopen("input","r",stdin);  
  5.     map<string,int> word_cnt;  
  6.     string str;  
  7.   
  8.     while (cin >> str)  
  9.     {  
  10.         ++ word_cnt[str];  
  11.     }  
  12.   
  13.     cout << "Word:\t\tTimes:" << endl;  
  14.     for (map<string,int>::iterator iter = word_cnt.begin(); iter != word_cnt.end(); ++iter)  
  15.     {  
  16.         cout << iter -> first << "\t\t" << iter -> second << endl;  
  17.     }  
  18. }  

五、map::insert的使用

map容器中,键影响了实参的类型,vector的差别:

    a.插入单个元素的insert版本使用键-pair类型的参数。类似地,对于参数为一对迭代器的版本,迭代器必须指向键-pair类型的元素。

    b. map容器的接受单个值的insert版本的返回类型。

map容器提供的insert操作

m.insert(e)

e是一个用在m上的value_type类型的值。如果键(e.first)不在m,则插入一个值为e.second的新元素;如果该键在m中已存在,则保持m不变。该函数返回一个pair类型对象,包含指向键为e.first的元素的map迭代器,以及一个bool类型的对象,表示是否插入了该元素

m.insert(b,e)

begend是标记元素范围的迭代器,其中的元素必须为m.value_type类型的键-值对。对于该范围内的所有元素,如果它的键在m中不存在,则将该键及其关联的值插入到m返回void类型

m.insert(iter,e)

e是一个用在m上的value_type类型的值。如果键(e.first)不在m,则创建新元素,以迭代器iter为起点搜索新元素存储的位置。返回一个迭代器,指向m中具有给定键的元素


1、以insert代替下标运算

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //创建一个pair对象,将之直接插入到map容器中  
  2. word_cnt.insert(map<string,int>::value_type("Anna",1));  

使用insert成员可以有效的避免下标操作符带来的副作用:不必要的初始化。

传递给insert的实参相当笨拙,可以使用两种方法简化:

    1)使用make_pair

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. word_cnt.insert(make_pair("Anna",1));  

    2)使用typedef

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. typedef map<string,int>::value_type valType;  
  2. word_cnt.insert(valType("Anna",2));  

2、检测insert的返回值

    带有一个键-pair形参insert版本将返回一个值:包含一个迭代器和一个bool值的pair对象,其中迭代器指向map中具有相应键的元素,bool值则表示是否插入了该元素。如果该键已在容器中,则其关联的值保持不变,返回的bool值为false,如果该键并不存在于容器中,则bool值为trul。在这两种情况下,迭代器都将指向具有给定键的元素。

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //重写前面的字符统计函数  
  2. map<string,int> word_cnt;  
  3. string word;  
  4.   
  5. while (cin >> word)  
  6. {  
  7.     pair<map<string,int>::iterator,bool> res = word_cnt.insert(make_pair(word,1));  
  8.   
  9.     if (!res.second)  
  10.         ++ res.first -> second;  
  11. }  

3、语法展开【解释上面的例程】

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. pair<map<string,int>::iterator,bool> res = word_cnt.insert(make_pair(word,1));  

对于:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ++res.first -> second;  

可以理解为:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ++((res.first) -> second);  

这个表达式一步步地展开解释:

    1)res存储insert函数返回的pair对象。pairfirst成员是一个map迭代器,指向插入的键

    2)res.firstinsert返回的pair对象中获取map迭代器。

    3)res.first-> second对该迭代器进行解引用,获得一个value_type类型的对象。这个对象同样是pair类型的,它的second成员即为我们所添加的元素的值部分。

    4)++res.first -> second实现该值的自增运算。



STL实践与分析

--map类型(下)



六、查找并读取map中的元素

map容器提供了两个操作:countfind,用于检查某个键是否存在而不会插入该键:

不修改map对象的查询

m.count(k)

返回mk的出现次数

m.find(k)

如果m容器中存在k索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端的迭代器

1、使用count检查map对象中某键是否存在

    因为map容器只允许一个键对应一个实例,所以,对于map对象,count成员的返回值只能是10

    如果返回值非0,则可以使用下标操作符来获取该键所关联的值,而不必担心这样会在map容器中插入新元素:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int occurs = 0;  
  2. if (word_cnt.count("Dream"))  
  3. {  
  4.     occurs = word_cnt["Dream"];  
  5. }  
  6. cout << occurs << endl;  

2、读取元素而不插入元素

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int occurs = 0;  
  2. map<string,int>::iterator iter = word_cnt.find("Dream");  
  3. if (iter != word_cnt.end())  
  4. {  
  5.     occurs = iter -> second;  
  6. }  
  7. cout << occurs << endl;  

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //P316 习题10.16  
  2. map<string,vector<int> > str_vec;  
  3. map<string,vector<int> >::iterator iter = str_vec.find("dream");  

七、从map对象中删除元素

map对象中删除元素

m.erase(K)

删除m中键为K的元素。返回size_type类型的值,表示删除的元素个数

m.erase(p)

m中删除迭代器p所指向的元素。p必须指向m中确实存在的元素,而且不能等于m.end()。返回void类型

m.erase(b,e)

m中删除一段范围内的元素,该范围由迭代器对be标记。be必须标记m中的一段有效范围:be都必须指向m中的元素或最后一个元素的下一个位置。而且,be要么相等(此时删除的范围为空),要么b所指向的元素必须出现在e所指向的元素之前。返回void类型


[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int remCount = 0;  
  2. if (remCount = word_cnt.erase("Dream"))  
  3. {  
  4.     cout << "Have removed " << remCount << " words" << endl;  
  5. }  
  6. else  
  7. {  
  8.     cout << "Not found!" << endl;  
  9. }  

erase函数返回被删除元素的个数。对于map容器,该值必然是0或者1。如果返回0值,则表示该元素并不存在于map容器中。


八、map对象的迭代遍历

    与其他容器一样,map同样提供了beginend运算,以生成用于遍历整个容器的迭代器:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. cout << "First:\t\tSecond:" << endl;  
  2. map<string,int>::iterator iter = word_cnt.begin();  
  3. while (iter != word_cnt.end())  
  4. {  
  5.     cout << iter -> first << "\t\t" << iter -> second << endl;  
  6.     ++ iter;  
  7. }  

或:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. cout << "First:\t\tSecond:" << endl;  
  2. typedef map<string,int>::iterator mapIter;  
  3. for (mapIter iter = word_cnt.begin(); iter != word_cnt.end(); ++iter)  
  4. {  
  5.     cout << iter -> first << "\t\t" << iter -> second << endl;  
  6. }  

在使用迭代器遍历map容器时,迭代器指向的元素按照键的升序排列


九、“单词转换”map对象

1、问题:

给出一个string对象,把它转换为另一个string对象。本程序的输入是两个文件。第一个文件包括了若干单词对,每对的第一个单词将出现在输入的字符串中,而第二个单词则是用于输出。本质上,这个文件提供的是单词转换的集合——在遇到第一个单词时,应该将之替换为第二个单词。第二个文件则提供了需要转换的文本。如果单词转换文件的内容是:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 'em them   
  2. cuz because   
  3. gratz grateful   
  4. i I   
  5. nah no  
  6. pos supposed   
  7. sez said   
  8. tanx thanks   
  9. wuz was  

2、测试样例

    而要转换的文本是:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. nah i sez tanx cuz i wuz pos to   
  2. not cuz i wuz gratz   

则程序将产生如下输出结果:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. no I said thanks because I was supposed to   
  2. not because I was grateful  

4、思路:

    将被替换的单词作为键,而用作的替换的单词则作为其相应要替换的值。


5、程序:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <iostream>  
  2. #include <sstream>  
  3. #include <fstream>  
  4. #include <map>  
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     ifstream inFile("input1");  
  10.     string line,firWord,secWord;  
  11.     map<string,string> convMap;  
  12.   
  13.     while (getline(inFile,line))  
  14.     {  
  15.         istringstream strItem(line);  
  16.         while (strItem >> firWord >> secWord)  
  17.         {  
  18.             convMap.insert(make_pair(firWord,secWord));  
  19.         }  
  20.     }  
  21.   
  22.     inFile.close();  
  23.     inFile.clear();  
  24.     inFile.open("input2");  
  25.   
  26.     string word;  
  27.     while (getline(inFile,line))  
  28.     {  
  29.         istringstream strItem(line);  
  30.         while (strItem >> word)  
  31.         {  
  32.             map<string,string>::iterator iter = convMap.find(word);  
  33.             if (iter != convMap.end())  
  34.             {  
  35.                 cout << iter -> second << ' ';  
  36.             }  
  37.             else  
  38.             {  
  39.                 cout << word << ' ';  
  40.             }  
  41.         }  
  42.         cout << endl;  
  43.     }  
  44. }  

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //附上书上的原程序  
  2. int main(int argc, char **argv)  
  3. {  
  4.     map<string, string> trans_map;  
  5.     string key, value;  
  6.     if (argc != 3)  
  7.         throw runtime_error("wrong number of arguments");  
  8.   
  9.     ifstream map_file;  
  10.     if (!open_file(map_file, argv[1]))  
  11.         throw runtime_error("no transformation file");  
  12.   
  13.     while (map_file >> key >> value)  
  14.         trans_map.insert(make_pair(key, value));  
  15.   
  16.     ifstream input;  
  17.     if (!open_file(input, argv[2]))  
  18.         throw runtime_error("no input file");  
  19.     string line;  
  20.     while (getline(input, line))  
  21.     {  
  22.         istringstream stream(line);  
  23.         string word;  
  24.         bool firstword = true;  
  25.         while (stream >> word)  
  26.         {  
  27.             map<string, string>::const_iterator map_it =  
  28.                 trans_map.find(word);  
  29.             if (map_it != trans_map.end())  
  30.                 word = map_it->second;  
  31.             if (firstword)  
  32.                 firstword = false;  
  33.             else  
  34.                 cout << " ";  
  35.             cout << word;  
  36.         }  
  37.         cout << endl;  
  38.     }  
  39.     return 0;  
  40. }  

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //P319 习题10.18  
  2. #include <iostream>  
  3. #include <fstream>  
  4. #include <string>  
  5. #include <map>  
  6. #include <vector>  
  7. using namespace std;  
  8.   
  9. int main()  
  10. {  
  11.     typedef string Family;  
  12.     typedef vector<string> Names;  
  13.     map<Family,Names> pepMap;  
  14.   
  15.     Family first;  
  16.     string second;  
  17.     ifstream inFile("input");  
  18.     while (inFile >> first >> second)  
  19.     {  
  20.         Names child;  
  21.         pair<map<Family,Names>::iterator,bool> res = pepMap.insert(make_pair(first,child));  
  22.         res.first -> second.push_back(second);  
  23.     }  
  24.   
  25.     inFile.close();  
  26.     inFile.clear();  
  27.     inFile.open("test");  
  28.   
  29.     string searchFamily;  
  30.     while (inFile >> searchFamily)  
  31.     {  
  32.         map<Family,Names>::iterator iter = pepMap.find(searchFamily);  
  33.   
  34.         if (iter == pepMap.end())  
  35.         {  
  36.             cout << "NO this Family name" << endl;  
  37.         }  
  38.         else  
  39.         {  
  40.             cout << iter -> first << ":" << endl;  
  41.             Names::iterator itName = iter -> second.begin();  
  42.             while (itName != iter -> second.end())  
  43.             {  
  44.                 cout << *itName << endl;  
  45.                 ++ itName;  
  46.             }  
  47.             cout << endl;  
  48.         }  
  49.     }  
  50. }  


本文借鉴:http://blog.csdn.net/column/details/zjf666.html?&page=4


0 0
原创粉丝点击