Map容器

一、Map

(1)定義

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可實現這個功能
編程

(2)底層實現

Map內部自建一顆紅黑樹(一種非嚴格意義上的平衡二叉樹),這顆樹具備對數據自動排序的功能,因此在map內部全部的數據都是有序的,後邊咱們會見識到有序的好處。
數組

因爲STL是一個統一的總體,map的不少用法都和STL中其它的東西結合在一塊兒,好比在排序上,默認用的是小於號,即less<>。 數據結構

Map中因爲它內部有序,由紅黑樹保證,所以不少函數執行的時間複雜度都是log2N的,若是用map函數能夠實現的功能,而STL  Algorithm也能夠完成該功能,建議用map自帶函數,效率高一些。 less

Map在空間上的特性,因爲map的每一個數據對應紅黑樹上的一個節點,這個節點在不保存你的數據時,是佔用16個字節的,一個父節點指針,左右孩子指針,還有一個枚舉值(標示紅黑的,至關於平衡二叉樹中的平衡因子),這些地方很費內存。函數

(3)特色

Map相似於數據庫中的1:1關係,它是一種關聯容器,提供一對一(C++ primer中文版中將第一個譯爲鍵,每一個鍵只能在map中出現一次,第二個被譯爲該鍵對應的值)的數據處理能力,這種特性了使得map相似於數據結構裏的紅黑二叉樹。Multimap相似於數據庫中的1:N關係,它是一種關聯容器,提供一對多的數據處理能力。性能

(4)性能分析

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函數和比較函數

二、成員函數

(1)構造函數

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中每一個節點由一對值構成。

(2)插入操做

(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;
}
}

(3)刪除操做

(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;
}

(4)定位查找

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;
}
}

(5)數據大小

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;
}

(6)遍歷操做

第一種:應用前向迭代器
第二種:應用反相迭代器
第三種:用數組方式

示例:

#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;
}
}

(7)排序操做

第一種:小於號重載
第二種:仿函數的應用,這個時候結構體中沒有直接的小於號重載

示例:

#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));
}
相關文章
相關標籤/搜索