關聯容器概述

關聯容器和順序容器有着根本的不一樣:關聯容器中的元素是按關鍵字來保存和訪問的。與之相對,順序容器中的元素是按它們在容器中的位置來順序保存和訪問的。算法

雖然關聯容器的不少行爲與順序容器相同,但其不一樣之處反映了關鍵字的做用。數組

關聯容器支持高效的關鍵字查找和訪問。兩個主要的關聯容器類型是map和set。map中的元素是一些關鍵字-值對:關鍵字起到索引的做用,值則表示與索引相關聯的數據。set中每一個元素包含一個關鍵字;set支持高效的關鍵字查詢操做——檢查一個給定關鍵字是否在set中。例如,在某些文本處理過程當中,能夠用一個set來保存想要忽略的單詞。字典則是一個很好的使用map的例子:能夠將單詞做爲關鍵字,將單詞釋義做爲值。函數

標準庫提供8個關聯容器,以下表所示。這8個容器間的不一樣體如今三個維度上:每一個容器(1)或者是一個set,或者是一個map;(2)或者要求不重複的關鍵字,或者容許重複關鍵字;(3)按順序保存元素,或無序保存。容許重複關鍵字的容器的名字中都包含單詞multi;不保存關鍵字按順序存儲的容器的名字都以單詞unordered開頭。所以一個unordered_multi_set是一個容許重複關鍵字,元素無序保存的集合,而一個set則是一個要求不重複關鍵字,有序存儲的集合。無序容器使用哈希函數來組織元素。性能

類型map和multimap定義在頭文件map中;set和multiset定義在頭文件set中;無序容器則定義在頭文件unordered_map和unordered_set中。spa

關聯容器類型

按關鍵字有序保存元素指針

map         關聯數組:保存關鍵字-值對code

set         關鍵字即值,即只保存關鍵字的容器對象

multimap      關鍵字可重複出現的mapblog

multiset         關鍵字可重複出現的set排序

無序容器

unordered_map   用哈希函數組織的map

unordered_set    用哈希函數組織的set

unordered_multimap  哈希組織的map;關鍵字能夠重複出現

unordered_multiset    哈希組織的set;關鍵字能夠重複出現

 

1 關聯容器概述

關聯容器不支持順序容器的位置相關的操做,例如push_front或push_back。緣由是關聯容器中元素是根據關鍵字存儲的,這些操做對關聯容器沒有意義。並且,關聯容器也不支持構造函數或插入操做這些接受一個元素值和一個數量值的操做。

除了順序容器相同的操做以外,關聯容器還支持一些順序容器不支持的操做和類型別名。此外,無序容器還提供了一些用來調整哈希性能的操做。

關聯容器的迭代器都是雙向的

 

定義關聯容器

當定義一個map時,必須既指明關鍵字類型又指明值類型。而定義一個set時,只需指明關鍵字類型,由於set中沒有值。每一個關聯容器都定義了一個默認構造函數,它建立一個指定類型的空容器,咱們也能夠將關聯容器初始化爲另外一個同類型容器的拷貝,或是從一個值範圍來初始化關聯容器,只要這些值能夠轉化爲容器中所需類型就能夠。在新標準下,咱們也能夠對關聯容器進行值初始化:

map<string,size_t> word_count ; //空容器

//列表初始化

set<string> exclude={"the","but","and","or","an","a","The","But","And","Or","An","A"};

//三個元素;authors將姓映射到名

map<string,string> autors={ {"Joyce","James"},

              {"Austen","Jane"},

              {"Dickens","Charles"}};

與以往同樣,初始化器必須能轉換爲容器中元素的類型。對於set,元素類型就是關鍵字類型。

當初始化一個map時,必須提供關鍵字類型和值類型,咱們將每一個關鍵字-值對包圍在花括號中:

 {key,value}

來指出它們一塊兒構成了map中的一個元素。在每一個花括號中,關鍵字是第一個元素,值是第二個。所以,anthors將姓映射到名,初始化它包括三個元素。

 

初始化multimap或multiset

一個map或set中的關鍵字必須是惟一的,即,對於一個給定的關鍵字,只能有一個元素的關鍵字等於它。容器multimap和multiset沒有此限制,它們都容許多個元素具備相同的關鍵字。

 

 

關鍵字類型的要求

關聯容器對其關鍵字類型有一些限制。對於無序容器中關鍵字的要求,咱們後面介紹。對於有序容器——map、multimap、set以及multiset,關鍵字類型必須定義元素比較的方法。默認狀況下,標準庫使用關鍵字類型的<運算符來比較兩個關鍵字。在集合類型中,關鍵字類型就是元素類型,在映射類型中,關鍵字類型是元素的第一部分的類型。所以,word_count的關鍵字類型是string。相似的,exclude的關鍵字類型也是string。

 

有序容器的關鍵字類型

能夠向一個算法提供咱們本身定義的比較操做,與之相似,也能夠提供本身定義的操做來代替關鍵字上的<運算符。所提供的操做必須在關鍵字類型上定義一個嚴格弱序。能夠將嚴格弱序看做「小於等於」,雖然實際定義的操做多是一個複雜的函數。不管咱們怎樣定義比較函數,它必須具有以下基本性質:

  • 兩個關鍵字不能同時「小於等於」對方:若是k1「小於等於」k2,那麼k2毫不能「小於等於」k1.
  • 若是k1「小於等於」k2,且k2「小於等於」k3,那麼k1必須「小於等於」k3
  • 若是存在兩個關鍵字,任何一個都不「小於等於」另外一個,那麼咱們稱這兩個關鍵字是「等價」的。若是k1「等價與」k2,且k2「等價於」k3,那麼k1必須「等價於」k3.

若是兩個關鍵字是等價的(即,任何一個都不「小於等於」另外一個),那麼容器將它們視做相等來處理。當用做map關鍵字時,只能有一個元素與這個關鍵字關聯,咱們能夠用二者中任意一個來訪問對應的值。

 

使用關鍵字的比較函數

用來組織一個容器中元素的操做的類型也是容器類型的一部分。爲了指定使用自定義的操做,必須在定義關聯容器類型時提供此操做的類型。如前所述,用尖括號指出要定義哪一種類型的容器,自定義的操做類型必須在尖括號中緊跟這元素類型給出。

在尖括號中出現的每一個類型,就僅僅是一個類型而已。當咱們建立一個容器(對象)時,纔會以構造函數參數的形式提供真正的比較操做(其類型必須與在尖括號中指定的類型相吻合)。

例如,咱們不能直接定義一個Sales_data的multiset,由於Sales_data沒有<運算符。可是,能夠用定義的compareIsbn函數來定義一個multiset。此函數在Sales_data對象的ISBN成員上定義了一個嚴格弱序。函數compareIsbn應該像下面這樣定義

bool compareIsbn(const Sales_data &lhs,const Sales_data &rhs)

{

  return lhs.isbn()<rhs.isbn();

}

爲了使用本身定義的操做,在定義multiset時咱們必須提供兩個類型。關鍵字類型Sales_data,以及比較操做類型——應該是一種函數指針類型,能夠指向compareIsbn。當定義此容器類型的對象時,須要提供想要使用的操做的指針。在本例中,咱們提供一個指向compareIsbn的指針:

//bookstore中多條記錄能夠有相同的ISBN

//bookstore中的元素以ISBN的順序進行排序

multiset<Sales_data,decltype(compareIsbn)*> bookstore(compareIsbn);

此處,咱們使用decltype來指出自定義操做的類型。記住,當用decltype來得到一個函數指針類型時,必須加上一個*來指出咱們要使用一個給定函數類型的指針。用compareIsbn來初始化bookstore對象,這表示當咱們向bookstore添加元素時,經過compareIsbn來爲這些元素排序。即,bookstore中的元素將按它們的ISBN成員的值排序。能夠用compareIsbn代替&compareIsbn做爲構造函數的參數,由於當咱們使用一個函數的名字時,在須要的狀況下它會自動轉化爲一個指針。固然,使用&compareIsbn的效果也是同樣的。

 

pair類型

在介紹關聯容器操做以前,咱們須要瞭解名爲pair的標準庫類型,它定義在頭文件utility中。

一個pair保存兩個數據成員。相似容器,pair是一個用來生成特定類型的模板。當建立一個pair時,咱們必須提供兩個類型名,pair的數據成員將具備對應的類型。兩個類型不要求同樣:

 pair<string,string> anon;  //保存兩個string

pair<string,size_t> word_count;   //保存一個string和一個size_t

pair<string,vector<int>> line;  //保存string和vector<int>

pair的默認構造函數對數據成員進行值初始化。所以,anon是一個包含兩個空string的pair,line保存一個空string和一個空vector。word_count中的size_t 成員值爲0,而string成員被初始化爲空vector。

咱們也能夠爲每一個成員提供初始化器:

pair<string,string> author{"James","Joyce"};

這條語句建立了一個名爲author的pair,兩個成員被初始化爲"James"和"Joyce"。

與其餘標準庫類型不一樣,pair的數據成員是public的。兩個成員分別命名爲first和second。咱們用普通的成員訪問符號來訪問它們,例如

cout<<w.first<<" occurs "<<w.second<<endl;

此處,w 是指向某個元素的引用。map的元素是pair。在這條語句中,咱們首先打印關鍵字——元素的first成員,接着打印計數器——second成員。標準庫只定義了有限的幾個pair操做,下表列出了這些操做:

pair上的操做

pair<T1,T2> p;              p是一個pair,兩個類型分別爲T1和T2的成員都進行了值初始化

pair<T1,T2> p(v1,v2)          p是一個成員類型爲T1和T2的pair;first和second成員分別用v1和v2進行初始化

pair<T1,T2> p={v1,v2};         等價於p(v1,v2)

make_pair(v1,v2)            返回一個用v1和v2初始化的pair。pair的類型從v1和v2的類型推斷出來

p.first                  返回p的名爲first的(公有)數據成員

p.second                 返回p的名爲second的(公有)數據成員

p1 relop p2                關係運算符(<、>、<=、>=)按字典序定義;例如,當p1.first<p2.first或!(p2.first<p1.first)&&p1.second<p2.second成立時,p1<p2爲

                   true。關係運算符利用元素的<運算符來實現

p1==p2                 當first和second成員分別相等時,兩個pair相等。相等性判斷利用元素的==運算符實現

p1!=p2                

 

建立pair對象的函數

想象有一個函數須要返回一個pair。在新標準下,咱們能夠對返回在進行列表初始化

pair<string,int>
process(vector<string> &v)
{
    //處理v
    if(!v.empty())
        return {v.back(),v.back().size()); // 列表初始化
    else
        return pair<string,int>();  //隱式構造返回值
}

在較早的C++版本中,不容許用花括號包圍的初始化器來返回pair這種類型的對象,必須顯示構造返回值:

if(!v.empty())

  return pair<string,int>(v.back(),v.back().size());

咱們還能夠用make_pair來生成pair對象,pair的兩個類型來自於make_pair的參數:

if(!v.empty())

  return make_pair(v.back(),v.back().size());

相關文章
相關標籤/搜索