Map是標準關聯式容器(associative container)之一,一個map是一個鍵值對序列,即(key ,value)對。它提供基於key的快速檢索能力,在一個map中key值是惟一的。map提供雙向迭代器,即有從前日後的(iterator),也有從後往前的(reverse_iterator)。ios
Map要求能對key進行<操做,且保持按key值遞增有序,所以map上的迭代器也是遞增有序的。若是對於元素並不須要保持有序,可使用hash_map。
數據庫
Map中key值是惟一的,若是已存在一個鍵值對(name,code):("name_a",1),而咱們還想插入一個鍵值對("name_a",1)則會報錯(不是報錯,準確的說是,返回插入不成功!)。而咱們又的確想這樣作,即一個鍵對應多個值,multimap可實現這個功能。
編程
Map內部自建一顆紅黑樹(一種非嚴格意義上的平衡二叉樹),這顆樹具備對數據自動排序的功能,因此在map內部全部的數據都是有序的,後邊咱們會見識到有序的好處。
數組
因爲STL是一個統一的總體,map的不少用法都和STL中其它的東西結合在一塊兒,好比在排序上,默認用的是小於號,即less<>。 數據結構
Map中因爲它內部有序,由紅黑樹保證,所以不少函數執行的時間複雜度都是log2N的,若是用map函數能夠實現的功能,而STL Algorithm也能夠完成該功能,建議用map自帶函數,效率高一些。 less
Map在空間上的特性,因爲map的每一個數據對應紅黑樹上的一個節點,這個節點在不保存你的數據時,是佔用16個字節的,一個父節點指針,左右孩子指針,還有一個枚舉值(標示紅黑的,至關於平衡二叉樹中的平衡因子),這些地方很費內存。函數
Map相似於數據庫中的1:1關係,它是一種關聯容器,提供一對一(C++ primer中文版中將第一個譯爲鍵,每一個鍵只能在map中出現一次,第二個被譯爲該鍵對應的值)的數據處理能力,這種特性了使得map相似於數據結構裏的紅黑二叉樹。Multimap相似於數據庫中的1:N關係,它是一種關聯容器,提供一對多的數據處理能力。性能
a)、hash_map和map的區別在哪裏?測試
(1)構造函數 hash_map須要hash函數,等於函數;map只須要比較函數(小於函數)。spa
(2)存儲結構 hash_map採用hash表存儲,map通常採用紅黑樹實現。所以內存數據結構是不同的。
b)、何時須要使用hash_map,何時須要map?
整體來講,hash_map 查找速度會比map快,並且查找速度基本和數據數據量大小,屬於常數級別;而map的查找速度是log(n)級別。
但若你對內存使用特別嚴格,但願程序儘量少消耗內存,那麼必定要當心,hash_map可能會讓你陷入尷尬,特別是當你的hash_map對象特別多時,你就更沒法控制了,並且 hash_map的構造速度較慢。
所以,選擇的時候須要權衡三個因素: 查找速度,數據量,內存使用。
c)、如何用hash_map替換程序中已有的map容器?
這個很容易,但須要你有良好的編程風格。建議你儘可能使用typedef來定義你的類型:
typedef map KeyMap;
當你但願使用hash_map來替換的時候,只須要修改:
typedef hash_map KeyMap;
其餘的基本不變。固然,你須要注意是否有Key類型的hash函數和比較函數。
map(); // 默認構造函數 map(const map& m) // 拷貝構造函數 map(iterator begin, iterator end ); //區間構造函數 map(iterator begin, iterator end, const traits& _compare) //帶比較謂詞的構造函數 map(iterator begin, iterator end, const traits& _compare, const allocator& all) //帶分配器
通過分析發現,map的構造函數主要是調用「拷貝構造函數」和利用「迭代器」進行初始化兩種方式,由於map中每一個節點由一對值構成。
(1)、insert(pair<T1,T2,>(key1,value1)) //用insert函數插入pair數據 (2)、insert(map<T1,T2>::value_type(key1,value1)); //用insert函數插入value_type數據,這種插入方式和第一種基本類似 (3)、利用數組進行插入
示例:
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, 「student_one」)); mapStudent.insert(pair<int, string>(2, 「student_two」)); mapStudent.insert(pair<int, string>(3, 「student_three」)); map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent.insert(map<int, string>::value_type (1, 「student_one」)); mapStudent.insert(map<int, string>::value_type (2, 「student_two」)); mapStudent.insert(map<int, string>::value_type (3, 「student_three」)); map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent[1] = 「student_one」; mapStudent[2] = 「student_two」; mapStudent[3] = 「student_three」; map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
以上三種用法,雖然均可以實現數據的插入,可是它們是有區別的:第一種和第二種在效果上是同樣的,用insert函數插入數據,在數據的插入上涉及到集合的惟一性這個概念,即當map中有這個關鍵字時,insert操做是插入數據不了的,可是用數組方式就不一樣了,它能夠覆蓋之前該關鍵字對應的值,以下:
mapStudent.insert(map<int, string>::value_type (1, 「student_one」)); mapStudent.insert(map<int, string>::value_type (1, 「student_two」));
上面這兩條語句執行後,map中1這個關鍵字對應的值是「student_one」,第二條語句並無生效,那麼咱們怎麼知道insert語句是否插入成功,能夠用pair來得到是否插入成功,程序以下:
Pair<map<int, string>::iterator, bool> Insert_Pair; Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, 「student_one」));
咱們經過pair的第二個變量來知道是否插入成功,它的第一個變量返回的是一個map的迭代器,若是插入成功的話:Insert_Pair.second應該是true的,不然爲false。
下面給出完成代碼,演示插入成功與否問題:
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; Pair<map<int, string>::iterator, bool> Insert_Pair; Insert_Pair=mapStudent.insert(pair<int, string>(1, 「student_one」)); If(Insert_Pair.second == true) { Cout<<」Insert Successfully」<<endl; } Else { Cout<<」Insert Failure」<<endl; } Insert_Pair=mapStudent.insert(pair<int, string>(1, 「student_two」)); If(Insert_Pair.second == true) { Cout<<」Insert Successfully」<<endl; } Else { Cout<<」Insert Failure」<<endl; } map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
用以下程序,看下用數組插入在數據覆蓋上的效果:
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent[1] = 「student_one」; mapStudent[1] = 「student_two」; mapStudent[2] = 「student_three」; map<int, string>::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
(1)、erase(map<T1,T2>::iterator iter); //刪除迭代器所指的節點 (2)、erase(key k); //根據鍵值進行刪除,刪除鍵值k所指的節點 (3)、erase(map<T1,T2>::iteratormap iter1,<T1,T2>::iteratoriter2); //刪除iter1和iter2之間的數據。 clear(); //清空map中的數據能夠用clear()函數,斷定map中是否有數據能夠用empty()函數,它返回true則說明是空map
示例:
#pragma warning(disable:4786) #include <iostream> #include <string> #include <map> using namespace std; int main() { /* map<int,string> tmp; map<int,string>::const_iterator iter1,iter2; tmp.insert(pair<int,string>(54090104,"Bob")); tmp.insert(pair<int,string>(54090105,"Ben")); iter1 = tmp.begin(); iter2 = tmp.end(); */ map<int,string> studentMessage; map<int,string>::iterator iter; //向map中插入數據 studentMessage.insert(pair<int,string>(54090101,"Mike")); studentMessage.insert(pair<int,string>(54090101,"MIKE"));//重複插入 studentMessage.insert(map<int,string>::value_type(54090102,"Sam")); studentMessage.insert(map<int,string>::value_type(54090102,"SAM"));//重複插入 studentMessage[54090103] = "Jake"; studentMessage[54090103] = "JAKE";//重複插入 //爲了測試刪除,先插入兩個數據,看插入結果主要看上面的插入方式 studentMessage[54090104] = "Bob"; studentMessage[54090105] = "Ben"; cout<<"完成插入後map中的數據:"<<endl; for(iter = studentMessage.begin() ; iter != studentMessage.end() ; ++iter) { cout<<iter->first<<" "<<iter->second<<endl; } //從map中刪除數據 iter = studentMessage.begin(); studentMessage.erase(iter); cout<<"利用迭代器刪除map中第一個元素:"<<endl; for(iter = studentMessage.begin() ; iter != studentMessage.end() ; ++iter) { cout<<iter->first<<" "<<iter->second<<endl; } studentMessage.erase(54090102); cout<<"利用鍵值刪除map中的第一個元素:"<<endl; for(iter = studentMessage.begin() ; iter != studentMessage.end() ; ++iter) { cout<<iter->first<<" "<<iter->second<<endl; } studentMessage.erase(studentMessage.begin(),studentMessage.end()); cout<<"利用範圍迭代器刪除map中的全部數據:"<<endl; for(iter = studentMessage.begin() ; iter != studentMessage.end() ; ++iter) { cout<<iter->first<<" "<<iter->second<<endl; } return 0; }
begin(); //返回指向map頭部的迭代器 end(); //返回指向map末尾的迭代器 (1)、count(); //用count函數來斷定關鍵字是否出現,其缺點是沒法定位數據出現位置,因爲map的特性,一對一的映射關係,就決定了count函數的返回值只有兩個,要麼是0,要麼是1,出現的狀況,固然是返回1了 (2)、find(); //用find函數來定位數據出現位置,它返回的一個迭代器,當數據出現時,它返回數據所在位置的迭代器,若是map中沒有要查找的數據,它返回的迭代器等於end函數返回的迭代器 (3)、Lower_bound();Upper_bound(); //這個方法用來斷定數據是否出現,是顯得笨了點: //Lower_bound函數用法,這個函數用來返回要查找關鍵字的下界(是一個迭代器) //Upper_bound函數用法,這個函數用來返回要查找關鍵字的上界(是一個迭代器) //例如:map中已經插入了1,2,3,4的話,若是lower_bound(2)的話,返回的2,而upper-bound(2)的話,返回的就是3 Equal_range(); //函數返回一個pair,pair裏面第一個變量是Lower_bound返回的迭代器,pair裏面第二個迭代器是Upper_bound返回的迭代器,若是這兩個迭代器相等的話,則說明map中不出現這個關鍵字
示例:
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, 「student_one」)); mapStudent.insert(pair<int, string>(2, 「student_two」)); mapStudent.insert(pair<int, string>(3, 「student_three」)); map<int, string>::iterator iter; iter = mapStudent.find(1); if(iter != mapStudent.end()) { Cout<<」Find, the value is 」<<iter->second<<endl; } Else { Cout<<」Do not Find」<<endl; } }
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent[1] = 「student_one」; mapStudent[3] = 「student_three」; mapStudent[5] = 「student_five」; map<int, string>::iterator iter; iter = mapStudent.lower_bound(2); { //返回的是下界3的迭代器 Cout<<iter->second<<endl; } iter = mapStudent.lower_bound(3); { //返回的是下界3的迭代器 Cout<<iter->second<<endl; } iter = mapStudent.upper_bound(2); { //返回的是上界3的迭代器 Cout<<iter->second<<endl; } iter = mapStudent.upper_bound(3); { //返回的是上界5的迭代器 Cout<<iter->second<<endl; } Pair<map<int, string>::iterator, map<int, string>::iterator> mapPair; mapPair = mapStudent.equal_range(2); if(mapPair.first == mapPair.second) { cout<<」Do not Find」<<endl; } Else { Cout<<」Find」<<endl; } mapPair = mapStudent.equal_range(3); if(mapPair.first == mapPair.second) { cout<<」Do not Find」<<endl; } Else { Cout<<」Find」<<endl; } }
max_size(); //返回map容器可能包含的元素最大個數 size(); //返回當前map容器中的元素個數 count(); //用來查找map中某個某個鍵值出現的次數;
示例:
#pragma warning (disable:4786) #include <map> #include <string> #include <iostream> using namespace std; int main() { map<int,string> studentMessage; map<int,string>::iterator iter; studentMessage.insert(pair<int , string>(54090101,"Mike")); studentMessage.insert(pair<int , string>(54090102,"Sam")); studentMessage.insert(pair<int , string>(54090103,"Jake")); //begin獲取map中的第一個元素的迭代器,而且等於rend //end獲取map中的最後一個元素下一位置的迭代器,而且等於rbegin cout<<"迭代器中的元素以下:"<<endl; for(iter = studentMessage.begin() ; iter != studentMessage.end() ; ++iter) { cout<<iter->first<<" "<<iter->second<<endl; } //看看max_size和size的值得意義 cout<<"map 的 max_size 的值:"<<studentMessage.max_size()<<endl; cout<<"map 的 size 的值:"<<studentMessage.size()<<endl; //看看empty和clear的使用 studentMessage.clear(); if(studentMessage.empty()) { cout<<"The map is Empty !!"<<endl; } else { cout<<"The map is not Empty !!"<<endl; } return 0; }
第一種:應用前向迭代器 第二種:應用反相迭代器 第三種:用數組方式
示例:
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, 「student_one」)); mapStudent.insert(pair<int, string>(2, 「student_two」)); mapStudent.insert(pair<int, string>(3, 「student_three」)); map<int, string>::reverse_iterator iter; for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++) { Cout<<iter->first<<」 」<<iter->second<<end; } }
#include <map> #include <string> #include <iostream> Using namespace std; Int main() { Map<int, string> mapStudent; mapStudent.insert(pair<int, string>(1, 「student_one」)); mapStudent.insert(pair<int, string>(2, 「student_two」)); mapStudent.insert(pair<int, string>(3, 「student_three」)); int nSize = mapStudent.size() //此處有誤,應該是 for(int nIndex = 1; nIndex <= nSize; nIndex++) //by rainfish for(int nIndex = 0; nIndex < nSize; nIndex++) { Cout<<mapStudent[nIndex]<<end; } }
第一種:小於號重載 第二種:仿函數的應用,這個時候結構體中沒有直接的小於號重載
示例:
#include <map> #include <string> Using namespace std; Typedef struct tagStudentInfo { Int nID; String strName; }StudentInfo, *PStudentInfo; //學生信息 Int main() { int nSize; //用學生信息映射分數 map<StudentInfo, int>mapStudent; map<StudentInfo, int>::iterator iter; StudentInfo studentInfo; studentInfo.nID = 1; studentInfo.strName = 「student_one」; mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90)); studentInfo.nID = 2; studentInfo.strName = 「student_two」; mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80)); for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl; } 以上程序是沒法編譯經過的,只要重載小於號,就OK了,以下: Typedef struct tagStudentInfo { Int nID; String strName; Bool operator < (tagStudentInfo const& _A) const { //這個函數指定排序策略,按nID排序,若是nID相等的話,按strName排序 If(nID < _A.nID) return true; If(nID == _A.nID) return strName.compare(_A.strName) < 0; Return false; } }StudentInfo, *PStudentInfo; //學生信息
#include <map> #include <string> Using namespace std; Typedef struct tagStudentInfo { Int nID; String strName; }StudentInfo, *PStudentInfo; //學生信息 Classs sort { Public: Bool operator() (StudentInfo const &_A, StudentInfo const &_B) const { If(_A.nID < _B.nID) return true; If(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0; Return false; } }; Int main() { //用學生信息映射分數 Map<StudentInfo, int, sort>mapStudent; StudentInfo studentInfo; studentInfo.nID = 1; studentInfo.strName = 「student_one」; mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90)); studentInfo.nID = 2; studentInfo.strName = 「student_two」; mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80)); }