C++的容器類型能夠分爲順序容器和關聯容器兩大類。順序容器的知識能夠參看我上篇的隨筆《C++順序容器知識總結》。關聯容器支持經過鍵值來高效的查找和讀取元素,這是它和順序容器最大的區別。兩種基本的關聯容器類型是map和set。map的元素以鍵-值對的形式組織:鍵用做元素在map中的索引,而值則表示所存儲和讀取的數據。set僅包含一個鍵,並有效的支持關於某個鍵是否存在的查詢。下表是關聯容器的類型:html
map 關聯數組;元素經過鍵來存儲和讀取 set 大小可變的集合,支持經過鍵實現快速讀取 multimap 支持同一個鍵屢次出現的map類型 multiset 支持同一個鍵屢次出現的set類型
在開始介紹關聯容器以前,咱們須要瞭解一種與之相關的標準庫類型——pair類型,該類型定義在頭文件utilty中。下表是pair類型提供的操做。編程
pair<T1,T2> p1; 建立一個空的pair對象,它的兩個元素分別是T1和T2類型,採用值初始化 pair<T1,T2> p1(v1,v2); 建立一個pair對象,它的兩個元素分別是T1和T2類型,其中first成員初始化爲v2,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的數據成員
能夠看到,和容器同樣,pair也是一種模板類型。它的數據成員是公有的,分別命名爲first和second,只需點操做就能夠訪問其成員。其定義初始化的操做也很簡單,除了構造函數外,pair還提供了一個make_pair函數來建立pair對象,並賦值給已存在的pair對象。數組
pair<string,string> next_auth; string first,last; while(cin>>first>>last) next_auth=make_pair(first,last); //上面的賦值操做等效於下面這條語句 next_auth=pair<string,string>(first,last);
map是鍵-值對的集合。map類型能夠理解爲關聯數組:可使用鍵做爲下標來獲取一個值,正如內置數組類型同樣。map和set等關聯容器共享大部分順序容器的操做。關聯容器不提供front、push_front、pop_front、back、push_back和pop_back操做。app
在使用map對象以前,須要在頭文件中包含map頭。其定義示例以下:函數
#include<map> map<string,int> word_count;
此外,map還共有3種構造函數用於定義和初始化。spa
map<k,v> m; 建立一個名爲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對象的元素是鍵-值對,即每一個元素包含兩個部分:鍵以及由鍵關聯的值。vaule_type是存儲元素的鍵以及值得pair類型,並且鍵位const。下表爲map類定義的類型。code
map<K,V>::key_type 在map容器中,用做索引的鍵的類型 map<K,V>::mapped_type 在map容器中,鍵所關聯的值的類型 map<K,V>::value_type 一個pair類型。它的first元素具備const map<K,V>::key_type 類型,而second元素具備map<K,V>::mapped_type類型
注意對map迭代器進行解引用將產生的是pair類型的對象,它的first成員存放的是鍵,爲const,second成員存放的是值。htm
給map添加元素有兩種方式:一是使用insert成員實現。二是先用下標獲取元素,讓而後給獲取的元素賦值。對象
map使用下標和vector相似,返回的都是下標關聯的值,可是map的下標是鍵而不是遞增的數字。下面的程序很好的說明了這個特色。blog
map<string,int> word_count; word_count["Anna"]=1;
首先在word_count中查找鍵爲Anna的元素,沒有找到。接着將一個新的鍵-值對插入到word_count容器中,鍵爲Anna,值初始化爲0;最後會把值1賦值給鍵爲Anna的元素。咱們能夠看到,用下標訪問map中不存在的元素,會致使在map容器中添加一個新元素,它的鍵即爲該下標值。map的下標運算和vector下標運算相同:返回鍵相關聯的值。運用map容器的這些特色,可使編程編的很簡練。以下面記錄每一個單詞出現次數的例子:
map<string,int> word_count; string word; //統計word_count中某個單詞出現的次數 while(cin>>word) ++word_count;
map容器的insert使用的是pair類型的參數。以下表爲map容器提供的insert操做。
m.insert(e) e是一個用在m上的vaule_type類型的值。若是鍵e.first不在m中,則插入一個鍵爲e.first值爲e.seconde的元素。若是該鍵在m中已存在。則m保持不變。 該函數返回一個pair類型的對象,包含指向鍵爲e.first的元素的map迭代器,以及一個bool類型的對象,表示是否插入成功。 m.insert(beg,end) beg和end是標記元素範圍的迭代器,其中的元素必須爲m.value_type類型的鍵-值對。 對於該範圍內的素有元素,若是它的鍵在m中不存在,則將該鍵及其關聯的值插入m。返回void m.insert(iter,e) e是一個用在vaule_type類型的值。若是鍵不在m中,則建立新元素,並以迭代器iter爲起點搜索新元素存儲的位置。 返回一個迭代器,指向m中具備給定鍵的元素。
以下:
//方法一 word_count.insert(map<string,int>::value_type("Anna",1)); //方法二,使用make_pair word_count.inser(make_pair("Anna",1)); //方法三,使用typedef typedef map<string,int>::value_type valType; word_count.insert(valType("Anna",1));
map中下標讀取元素的缺點是當不存在該元素時會自動添加,有時這是咱們不但願看到的。因此map提供了另外兩個操做:count和find,用於檢查某個鍵是否存在而不會插入該鍵。
m.count(k) 返回m中k出現次數
m.find(k) 若是m容器中存在按k索引的元素,則返回指向該元素的迭代器。若是不存在,則返回超出末端迭代器
count成員的返回值只能是0或1,由於map值容許一個鍵對應一個實例。若是返回值爲非0,則能夠用下標操做來獲取該鍵所關聯的值。
int occurs=0; if(word_count.count("foobar")) occurs=word_count["foobar"];
find操做凡湖指向元素的迭代器,若是元素不存在,則返回end迭代器。
int occurs=0; map<string,int>::iterator it=word_count.find("foobar"); if(it!=word_count.end()) occurs=it->second;
從map容器中刪除元素用erase操做,它有三種變化形式,以下:
m.erase(k) 刪除m中鍵爲k的元素。返回size_type類型的值,表示刪除的元素個數
m.srase(p) 從m中刪除迭代器p指向的元素。p必須指向m中確實存在的元素,並且不能等於m.end()。返回void型
m.erase(b,e) 從m中刪除一段範圍內的元素,該範圍由迭代器對b和e標記。b和e必須標記m中的一段有效範圍:即b和e都必須指向m中的元素或最後元素的下一個位置
並且,b要麼在e的錢main,要麼和e相等。返回void
map和其餘容器同樣也提供begin和end運算。
map<string,int>::const_iterator map_it=word_count.begin(); while(map_it!=word_count.end()){ cout<<map_it->first<<"occurs" <<map_it->second<<"time"<<endl; ++map_it; }
set只是單純的鍵的集合。當只想知道一個值是否存在時,使用set容器是最合適的。set容器支持大多數map的操做,包括構造函數、insert、count、find、erase操做。可是不包括下標操做,沒有定義mapped_type類型。在set容器中value_type不是pair類型,而是與key_type相同的類型。與map同樣,set容器中存儲的鍵也是惟一的。
使用set以前必須包含set頭文件,set支持的操做基本與map提供的相同。
vector<int > ivec; for(vector<int>::size_type i=0;i!=10;++i){ ivec.push_back(i); ivec.push_back(i); } //用ivec初始化set set<int> iset(ivec.begin(),ivec.end()); cout<<ivec.size()<<endl; //輸出20 cout<<iset.size()<<end; //輸出10
//方法一,直接插入 set<string> set1; set1.insert("the"); //方法二,使用迭代器 set<string> set2; set2.insert(ivec.begin(),ivec.end());
set沒有下標操做,爲了經過鍵從set中獲取元素,可以使用find運算。若是僅是判斷某個元素是否存在,也可以使用count操做,返回值只能是1或0。
參考文獻
《C++ PRIMER》 中文版