C++Primer 5th:第十一章 关联容器
第十一章 关联容器
类型map
和multimap
定义在头文件map
中
set
和multiset
定义在头文件set
中
无序容器
定义在头文件unorderd_map
和unorderd_set
中
11.1 使用关联容器
使用map
关键字-值对的集合
关联数组(associative array)
1 2 3 4 5 6 7 8 map <string , size_t > word_count;string word;while (cin >> word) ++word_count[word];for (const auto &w : word_count) cout << w.first << " occures " << w.second << ((w.second > 1 ) ? " times" : " time" ) << endl ;
使用set
使用set
保存想忽略的单词,只对不在集合中的单词统计出现次数
1 2 3 4 5 6 7 map <string , size_t > word_count;set <string > exclude = {"The" , "But" , "And" , "Or" , "An" , "A" , "the" , "but" , "and" , "or" , "an" , "a" };string word;while (cin >> word) if (exclude.find(word) == exclude.end()) ++word_count[word];
11.2 关联容器概述 11.2.1 定义关联容器
初始化multimap
或multiset
1 2 3 4 5 6 7 8 9 10 vector <int > ivec;for (vector <int >::size_type i = 0 ; i != 10 ; ++i) { ivec.push_back(i); ivec.push_back(i); }set <int > iset (ivec.cbegin(), ivec.cend()) ;multiset <int > miset (ivec.cbegin(), ivec.cend()) ;cout << ivec.size() << endl ; cout << iset.size() << endl ; cout << miset.size() << endl ;
11.2.2 关键字类型的要求
对于有序容器map
, multimap
, set
以及multiset
,关键字类型必须定义元素比较的方法。
11.2.3 pair类型
头文件:utility
pair
的数据成员是:public
的
两个成员分别命名为first
和second
1 pair<string , string > author{"James" , "Joyce" };
创建pair
对象的函数
1 2 3 4 5 6 7 8 pair<string, int> process(vector<string> &v){ if (!v.empty()) return {v.back(), v.back().size()}; else return pair<string , int >(); }
11.3 关联容器操作 key_type
mapped_type
value_type
对于set
,与key_type
相同
对于map
,为pair<const key_type, mapped_type>
11.3.1 关联容器迭代器
map
1 2 3 4 5 6 7 8 map <string , size_t > word_count;auto map_it = word_count.begin();cout << map_it->first; cout << " " << map_it->second; ++map_it->second;
set 的迭代器是 const 的
1 2 3 4 5 6 set <int > iset = { 0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 };set <int >::iterator set_it = iset.begin();if (set_it != iset.end()) { cout << *set_it << endl ; }
遍历关联容器
1 2 3 4 5 6 7 map <string , size_t > word_count;auto map_it = word_count.cbegin();while (map_it != word_count.cend()){ cout << map_it->first << " occurs " << map_it->second << " times" << endl ; ++map_it; }
在实际编程中,如果真要对一个关联容器使用算法,要么将它当作一个源序列,要么当作一个目的位置。
11.3.2 添加元素
向map
添加元素
1 2 3 4 5 map <string , size_t > word_count; word_count.insert({ "word" , 1 }); word_count.insert(make_pair("word" , 1 )); word_count.insert(pair<string , int >("word" , 1 )); word_count.insert(map <string , int >::value_type("word" , 1 ));
检测insert
的返回值
对于不包含重复关键字的容器,添加单一元素的insert
返回一个pair
pair
的first
成员是一个迭代器,指向具有给定关键字的元素
second
成员是一个bool
值
向multiset
或multimap
添加元素
1 2 3 multimap <string , string > authors; authors.insert({"Barth, John" , "Sot-weed Factor" }); authors.insert({"Barth, John" ,"Lost in the Funhouse" });
11.3.3 删除元素 1 2 3 4 5 6 7 c.earse(k); c.earse(p); c.earse(b,e);
11.3.4 map
的下标操作
使用一个不在map
容器中的关键字作为下标,会添加一个具有此关键字的元素到map
中
1 2 map <string , size_t > word_count; word_count["Anna" ] = 1 ;
使用下标操作的返回值
map
的下标运算符返回的类型与解引用map
迭代器得到的类型不同
1 2 3 count << word_count["Anna" ]; ++word_count["Anna" ];cout << word_count["Anna" ];
11.3.5 访问元素
1 2 3 4 5 set <int > iset = { 0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 }; iset.find(1 ); iset.find(11 ); iset.count(1 ); iset.count(11 );
对map
使用find
代替下标操作
在multimap
或multiset
中查找元素
1 2 3 4 5 6 7 8 string search_item ("Alain de Botton" ) ; auto entries = author.count(search_item); auto iter = authors.find(search_item); while (entries){ cout << iter->second << endl ; ++iter; --entries; }
一种不同的,面向迭代器的解决方案
lower_bound
和upper_bound
如果两者返回相同的迭代器,则给定的关键字不在容器中
1 2 3 4 for (auto beg = authors.lower_bound(search_item), end = authors.upper_bound(search_item); beg != end; ++beg){ cout << beg->second << endl ; }
equal_range
函数
1 2 3 for (auto pos = authors.equal_range(search_item); pos.first != pos.second; ++pos.first){ cout << pos->first->second << endl ; }
11.3.6 一个单词转换的 map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <iostream> #include <string> #include <map> #include <fstream> #include <sstream> using namespace std ; map<string, string> buildMap(ifstream& map_file) { map <string , string > trans_map; string key; string value; while (map_file >> key && getline(map_file, value)){ if (value.size() > 1 ) trans_map[key] = value.substr(1 ); else throw runtime_error("no rule for " + key); } return trans_map; }const string & transform (const string & s, const map <string , string >& m) { auto map_it = m.find(s); if (map_it != m.cend()) return map_it->second; else return s; }void word_transform (ifstream& map_file, ifstream& input) { auto trans_map = buildMap(map_file); string text; while (getline(input, text)) { istringstream stream (text) ; string word; bool firstword = true ; while (stream >> word) { if (firstword) firstword = false ; else cout << " " ; cout << transform(word, trans_map); } cout << endl ; } }int main () { ifstream ifs1, ifs2; ifs1.open("map_file.txt" ); ifs2.open("input.txt" ); word_transform(ifs1, ifs2); ifs1.close(); ifs2.close(); system("pause" ); return 0 ; }
1 2 3 4 5 6 7 k okay? y why r are u you pic picture thk thanks! 18r later
1 2 3 where r u y dont u sent me a pic k thk 18r
11.4 无序容器
如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术
解决,就可以使用无序容器
使用无序容器
1 2 3 4 5 6 7 8 9 #include <unordered_map> unordered_map <string , size_t > word_count;string word;while (cin >> word) ++word_count[word];for (const auto & w : word_count) cout << w.first << " occurs " << w.second << ((w.second > 1 ) ? "times" : "time" ) << endl ;
管理桶
无序容器在存储组织上为一组桶,每个桶保存零个或多个元素
无序容器使用一个哈希函数将元素映射到桶
无序容器对关键字类型的要求
默认情况下,无序容器使用关键字类型的==
运算符比较元素
使用一个hash类型的对象来生成每个元素的哈希值
1 2 3 4 5 6 7 8 9 10 11 size_t hasher (const Sales_data &sd) { return hash<string >()(sd.isbn()); }bool eqOp (const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() == rhs.isbn(); }using SD_multiset = unordered_multiset <Sales_data, decltype (hasher)*, decltype (eqOp)*>;SD_multiset bookstore (42 , hasher, eqOp) ;