【C++ STL】Map和Multimap


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM