1、結構
Map和multimap將key/value pair(鍵值/實值 隊組)當作元素,進行管理。他們根據key的排序准則將元素排序。multimap允許重復元素,map不允許。
元素要求:
- key/value必須具有assigned(可賦值)和copyable(可復制的)性質。
- 對於排序而言,key必須具是comparable(可比較的)。
2、能力
典型情況下,set,multisets,map,multimap使用相同的內部結構,因此可以將set和multiset當成特殊的map和multimap,只不過set的value和key指向的是同一元素。
map和multimap會根據key對元素進行自動排序,所以根據key值搜尋某個元素具有良好的性能,但是如果根據value來搜尋效率就很低了。
同樣,自動排序使得你不能直接改變元素的key,因為這樣會破壞正確次序,要修改元素的key,必須先移除擁有key的元素,然后插入擁有新的key/value的元素,從迭代器的觀點來看,元素的key是常數,元素的value是可以直接修改的。
3、操作函數
3.1 構造、析構、拷貝
操作 |
效果 |
map c |
產生一個空的map/multimap不包含任何元素 |
map c(op) |
以op為排序准則,產生一個空的map/multimap |
map c1(c2) |
產生某個map/multimap的副本,所有元素都被拷貝 |
map c(beg,end) |
以區間[beg,end)內的元素產生一個map/multimap |
map c(beg,end,op) |
以op為排序准則,區間[beg,end)內的所有元素生成一個map/multimap |
c.~map() |
銷毀所有元素,釋放內存 |
map<Key,Elem> |
一個map/multimap,以less<> (operator <)為排序准則 |
map<Key,Elem,Op> |
一個map,以op為排序准則 |
3.2 非變動性操作
操作 |
效果 |
c.size() |
返回當前的元素數量 |
c.empty() |
判斷大小是否為零,等同於0 == size(),效率更高 |
c.max_size() |
返回能容納的元素最大數量 |
c1 == c2 |
判斷c1是否等於c2 |
c1 != c2 |
判斷c1是否不等於c2(等同於!(c1==c2)) |
c1 < c2 |
判斷c1是否小於c2 |
c1 > c2 |
判斷c1是否大於c2 |
c1 <= c2 |
判斷c1是否小於等於c2(等同於!(c2<c1)) |
c1 >= c2 |
判斷c1是否大於等於c2 (等同於!(c1<c2)) |
3.3 特殊搜尋函數
操作 |
效果 |
count(key) |
返回鍵值等於key的元素個數 |
find(key) |
返回鍵值等於key的第一個元素,沒找到返回end() |
lower_bound(key) |
返回鍵值為key之元素的第一個可安插位置,也就是鍵值>=key的第一個元素位置 |
upper_bound(key) |
返回鍵值為key之元素的最后一個可安插位置,也就是鍵值>key的第一個元素位置 |
equal_range(key) |
返回鍵值為key之元素的第一個可安插位置和最后一個可安插位置,也就是鍵值==key的元素區間 |
3.4 賦值
操作 |
效果 |
c1 = c2 |
將c2的元素全部給c1 |
c1.swap(c2) |
將c1和c2 的元素互換 |
swap(c1,c2) |
同上,全局函數 |
3.5 迭代器函數
map和multimap不支持元素直接存取,因此元素的存取通常是經過迭代器進行。不過例外的是,map提供下標操作,可直接存取元素。同樣,他們的迭代器是雙向迭代器,所有的元素的key都被視為常數,不能調用任何變動性操作,如remove(),要移除元素,只能用塔門提供的函數。
操作 |
效果 |
c.begin() |
返回一個隨機存取迭代器,指向第一個元素 |
c.end() |
返回一個隨機存取迭代器,指向最后一個元素的下一個位置 |
c.rbegin() |
返回一個逆向迭代器,指向逆向迭代的第一個元素 |
c.rend() |
返回一個逆向迭代器,指向逆向迭代的最后一個元素的下一個位置 |
3.6 元素的安插和移除
操作 |
效果 |
c.insert(elem) |
插入一個elem副本,返回新元素位置,無論插入成功與否。 |
c.insert(pos, elem) |
安插一個elem元素副本,返回新元素位置,pos為收索起點,提升插入速度。 |
c.insert(beg,end) |
將區間[beg,end)所有的元素安插到c,無返回值。 |
c.erase(elem) |
刪除與elem相等的所有元素,返回被移除的元素個數。 |
c.erase(pos) |
移除迭代器pos所指位置元素,無返回值。 |
c.erase(beg,end) |
移除區間[beg,end)所有元素,無返回值。 |
c.clear() |
移除所有元素,將容器清空 |
有三個不同的方法將value插入map:
- 運用value_type,為了避免隱式轉換,可以使用value_type明白傳遞正確型別。value_type是容器本身提供的型別定義
std::map<std::string,float> coll; ... coll.insert(std::map<std::string,float>::value_type("otto",22.3));
- 運用pair,另一個作法是直接運用pair<>。
std::map<std::string,float> coll; ... //use implicit conversion: coll.insert(std::pair<std::string,float>("otto",22.3)); //use no implicit conversion: coll.insert(std::pair<const std::string,float>("otto",22.3));
上述第一個insert()語句內的型別並不正確,所以會被轉換成真正的型別。
- 運用make_pair()
最方便的方法是直接運用make_pair()函數,這個函數根據傳入的兩個參數構造一個pair對象。
std::map<std::string,float> coll; ... coll.insert(std::make_pair("otto",22.3)); 下面是個簡單例子,檢查安插元素是否成功: std::map<std::string,float> coll; ... if (coll.insert(std::make_pair("otto",22.3)).second) { std::cout << "OK, could insert otto/22.3" << std::endl; } else { std::cout << "Oops, could not insert otto/22.3 " << "(key otto already exists)" << std::endl; }
如果要移除某個值為value的元素,使用erase()即可。
std::map<std::string,float> coll; ... //remove all elements with the passed key coll.erase(key);
如果multimap內喊重復元素,不能使用erase()來刪除這些元素的第一個,但是可以這么做:
typedef multimap<string.float> StringFloatMMap; StringFloatMMap coll; ... //remove first element with passed key StringFloatMMap::iterator pos; pos = coll.find(key); if (pos != coll.end()) { coll.erase(pos); }
這里使用成員函數的find()而非STL里面的find(),因為成員函數更快,然而不能使用成員函數find()來移除擁有某個value(而非某個key)的元素。
移除元素時,要當心心意外發生,當移除迭代器所指對象時,可能使迭代器失效。
typedef std::map<std::string,float> StringFloatMap; StringFloatMap coll; StringFloatMap::iterator pos; ... for (pos = coll.begin(); pos != coll.end(); ++pos) { if (pos->second == value) { coll. erase (pos); // RUNTIME ERROR !!! } }
對pos所指元素實施erase(),會使pos不再成為一個有效的迭代器,如果此后未對pos重新設值就使用pos,會出現異常。++pos就能導致一個未定義行為。下面是正確的刪除方法。
typedef std::map<std::string,float> StringFloatMap; StringFloatMap coll; StringFloatMap::iterator pos; ... //remove all elements having a certain value for (pos = c.begin(); pos != c.end(); ) { if (pos->second == value) { c.erase(pos++); } else { ++pos; } }
注意,pos++會指向下一個元素,但返回其原始值(指向原位置)的一個副本,因此,當erase()被調用,pos已經不指向那個即將被刪除的元素了。
4、map視為關聯數組
通常,關聯數組不提供直接存取,必須依靠迭代器,不過map是個例外,map提供下標操作符,可以直接存取元素。不過下標操作符的索引不是元素整數位置,而是元素的key。也就是說,索引可以是任何型別,而非局限的整數型別。
操作 |
效果 |
m[key] |
返回一個reference,指向鍵值為key的元素,如果該元素尚未存在,插入該元素。 |
和一般數組的區別不僅僅是索引型別,你不能使用一個錯誤的索引,如果你是用某個key為索引,容器尚未存在該元素,會自動安插該元素,新元素由default構造,所有基本型別都提供default構造函數,以零為初始值。
關聯數組的行為方式有優點,也有缺點:
優點是可以透過方便的接口向map安插新元素。
std::map<std::string,float> coll; // empty collection /*insert "otto"/7.7 as key/value pair *-first it inserts "otto"/float() *-then it assigns 7.7 */ coll["otto"] = 7.7;
其中的語句:coll["otto"] = 7.7;處理如下:
1.處理coll["otto"]:
--如果存在鍵值為“otto”的元素,以上式子返回該元素的reference。
--如果沒有任何元素的鍵值為“otto”,以上式子便為map自動安插一個新元素,鍵值key為“otto”,value通過default構造函數完成,並返回一個reference指向新元素。
2.將7.7賦值給value:
--緊接着,將7.7賦值給剛才返回的元素。
這樣,map之內就包含了一個鍵值(key)為“otto”的元素,其值(value)為7.7。
缺點就是你可能不小心誤置新元素。例如你想打印key為“otto”的元素值:
std::cout << coll[“ottto”] << endl;
它會安插一個鍵值為“ottto”的元素,然后打印其值,缺省情況下是0。它並不會告訴你拼寫錯誤,並且插入一個你可能不需要的元素。
這種插入方式比一般的map安插方式來得慢,因為在生成新元素的時候,需要使用default構造函數將新元素初始化,而這個值馬上又會被真正的value覆蓋。
5、示例代碼
5.1 map
// cont/mapl.cpp #include <iostream> #include <map> #include <string> using namespace std; int main() { /*create map/associative array *-keys are strings *-values are floats */ typedef map<string,float> StringFloatMap; StringFloatMap stocks; // create empty container //insert some elements stocks["BASF"] = 369.50; stocks["VW"] = 413.50; stocks["Daimler"] = 819.00; stocks["BMW"] = 834.00; stocks["Siemens"] = 842.20; //print all elements StringFloatMap::iterator pos; for (pos = stocks.begin(); pos != stocks.end(); ++pos) { cout << "stock: " << pos->first << "\t" << "price: " << pos->second << endl; } cout << endl; //boom (all prices doubled) for (pos = stocks.begin(); pos != stocks.end(); ++pos) { pos->second *= 2; } //print all elements for (pos = stocks.begin(); pos != stocks.end(); ++pos) { cout << "stock: " << pos->first << "\t" << "price: " << pos->second << endl; } cout << endl; /*rename key from "VW" to "Volkswagen" *-only provided by exchanging element */ stocks["Volkswagen"] = stocks["VW"]; stocks.erase("VW"); //print all elements for (pos = stocks.begin(); pos != stocks.end(); ++pos) { cout << "stock: " << pos->first << "\t" << "price: " << pos->second << endl; } }
輸出:
stock: BASF price: 369.5 stock: BMW price: 834 stock: Daimler price: 819 stock: Siemens price: 842.2 stock: VW price: 413.5 stock: BASF price: 739 stock: BMW price: 1668 stock: Daimler price: 1638 stock: Siemens price: 1684.4 stock: VW price: 827 stock: BASF price: 739 stock: BMW price: 1668 stock: Daimler price: 1638 stock: Siemens price: 1684.4 stock: Volkswagen price: 827
5.2 multimap
// cont/mmap1.cpp #include <iostream> #include <map> #include <string> #include <iomanip> using namespace std; int main() { //define multimap type as string/string dictionary typedef multimap<string,string> StrStrMMap; //create empty dictionary StrStrMMap dict; //insert some elements in random order dict.insert(make_pair("day","Tag")); dict.insert(make_pair("strange","fremd")); dict.insert(make_pair("car","Auto")); dict.insert(make_pair("smart","elegant")); dict.insert(make_pair("trait","Merkmal")); dict.insert(make_pair("strange","seltsam")); dict.insert(make_pair("smart","raffiniert")); dict.insert(make_pair("smart","klug")); dict.insert(make_pair("clever","raffiniert")); //print all elements StrStrMMap::iterator pos; cout.setf (ios::left, ios::adjustfield); cout << ' ' << setw(10) << "english " << "german " << endl; cout << setfil('-') << setw(20) << "" << setfil(' ') << endl; for (pos = dict.begin(); pos != dict.end(); ++pos) { cout << ' ' << setw(10) << pos>first.c_str() << pos->second << endl; } cout << endl; //print all values for key "smart" string word("smart"); cout << word << ": " << endl; for (pos = dict.lower_bound(word); pos != dict.upper_bound(word); ++pos) { cout << " " << pos->second << endl; } //print all keys for value "raffiniert" word = ("raffiniert"); cout << word << ": " << endl; for (pos = dict.begin(); pos != dict.end(); ++pos) { if (pos->second == word) { cout << " " << pos->first << endl; } } }
輸出:
english german -------------------- car Auto clever raffiniert day Tag smart elegant smart raffiniert smart klug strange fremd strange seltsam trait Merkmal smart: elegant raffiniert klug raffiniert: clever smart