C++中的容器類詳解

PengPengBlog發表於2017-04-24

C++中的容器類包括“順序儲存結構”和“關聯儲存結構”,前者包括vector,list,deque等;後者包括set,map,multiset,multimap等。若需要儲存的元素數在編譯器間就可以確定,可以使用陣列來儲存,否則,就需要用到容器類了。

1、vector

    連續儲存結構,每個元素在記憶體上是連續的;

    支援高效的隨機訪問和在尾端插入/刪除操作,但其他位置的插入/刪除操作效率低下;

2、deque

    連續儲存結構,即其每個元素在記憶體上也是連續的,類似於vector,不同之處在於,deque提供了兩級陣列結構,第一級完全類似於vector,代表實際容器;另一級維護容器的首位地址。

    這樣,deque除了具有vector的所有功能外,還支援高效的首端插入/刪除操作。

3、list

    非連續儲存結構,具有雙連結串列結構,每個元素維護一對前向和後向指標,因此支援前向/後向遍歷。

    支援高效的隨機插入/刪除操作,但隨機訪問效率低下,且由於需要額外維護指標,開銷也比較大。

4、vector V.S. list V.S. deque:

    a、若需要隨機訪問操作,則選擇vector;

    b、若已經知道需要儲存元素的數目, 則選擇vector;

    c、若需要隨機插入/刪除(不僅僅在兩端),則選擇list

    d、只有需要在首端進行插入/刪除操作的時候,才選擇deque,否則都選擇vector。

    e、若既需要隨機插入/刪除,又需要隨機訪問,則需要在vector與list間做個折中。

    f、當要儲存的是大型負責類物件時,list要優於vector;當然這時候也可以用vector來儲存指向物件的指標,同樣會取得較高的效率,但是指標的維護非常容易出錯,因此不推薦使用。

5、capacity V.S size

    a、capacity是容器需要增長之前,能夠盛的元素總數;只有連續儲存的容器才有capacity的概念(例如vector,deque,string),list不需要capacity。

    b、size是容器當前儲存的元素的數目。

    c、vector預設的容量初始值,以及增長規則是依賴於編譯器的。

6、用vector儲存自定義類物件時,自定義類物件須滿足:

    a、有可供呼叫的無參建構函式(預設的或自定義的);

    b、有可用的拷貝賦值函式(預設的或自定義的)

7、迭代器iterator

    a、vector與deque的迭代器支援算術運算,list的迭代器只能進行++/--操作,不支援普通的算術運算。

以下為整個列表概述:

標準容器類

說明

順序性容器

vector

從後面快速的插入與刪除,直接訪問任何元素

deque

從前面或後面快速的插入與刪除,直接訪問任何元素

list

雙連結串列,從任何地方快速插入與刪除

關聯容器

set

快速查詢,不允許重複值

multiset

快速查詢,允許重複值

map

一對多對映,基於關鍵字快速查詢,不允許重複值

multimap

一對多對映,基於關鍵字快速查詢,允許重複值

容器介面卡

stack

後進先出

queue

先進先出

priority_queue

最高優先順序元素總是第一個出列

所有標準庫共有函式

預設建構函式

提供容器預設初始化的建構函式。

複製建構函式

將容器初始化為現有同類容器副本的建構函式

解構函式

不再需要容器時進行記憶體整理的解構函式

empty

容器中沒有元素時返回true,否則返回false

max_size

返回容器中最大元素個數

size

返回容器中當前元素個數

operator=

將一個容器賦給另一個容器

operator<

如果第一個容器小於第二個容器,返回true,否則返回false,

operator<=

如果第一個容器小於或等於第二個容器,返回true,否則返回false

operator>

如果第一個容器大於第二個容器,返回true,否則返回false

operator>=

如果第一個容器大於或等於第二個容器,返回true,否則返回false

operator==

如果第一個容器等於第二個容器,返回true,否則返回false

operator!=

如果第一個容器不等於第二個容器,返回true,否則返回false

swap

交換兩個容器的元素

其中operator>,operator>=,operator<,operator<=,operator==,operator!=均不適用於priority_queue

順序容器和關聯容器共有函式

begin

該函式兩個版本返回iterator或const_iterator,引用容器第一個元素

end

該函式兩個版本返回iterator或const_iterator,引用容器最後一個元素後面一位

rbegin

該函式兩個版本返回reverse_iterator或const_reverse_iterator,引用容器最後一個元素

rend

該函式兩個版本返回reverse_iterator或const_reverse_iterator,引用容器第一個元素前面一位

erase

從容器中清除一個或幾個元素

clear

清除容器中所有元素

下表顯示了順序容器和關聯容器中常用的typedef,這些typedef常用於變數、引數和函式返回值的一般性宣告。

value_type

容器中存放元素的型別

reference

容器中存放元素型別的引用

const_reference

容器中存放元素型別的常量引用,這種引用只能讀取容器中的元素和進行const操作

pointer

容器中存放元素型別的指標

iterator

指向容器中存放元素型別的迭代器

const_iterator

指向容器中存放元素型別的常量迭代器,只能讀取容器中的元素

reverse_iterator

指向容器中存放元素型別的逆向迭代器,這種迭代器在容器中逆向迭代

const_reverse_iterator

指向容器中存放元素型別的逆向迭代器,只能讀取容器中的元素

difference_type

引用相同容器的兩個迭代器相減結果的型別(list和關聯容器沒有定義operator-)

size_type

用於計算容器中專案數和檢索順序容器的型別(不能對list檢索)

 

8.序列類容器

(1)vector向量相當於一個陣列

    在記憶體中分配一塊連續的記憶體空間進行儲存。支援不指定vector大小的儲存。STL內部實現時,首先分配一個非常大的記憶體空間預備進行儲存,即capacity()函式返回的大小,當超過此分配的空間時再整體重新放分配一塊記憶體儲存,這給人以vector可以不指定vector即一個連續記憶體的大小的感覺。通常此預設的記憶體分配能完成大部分情況下的儲存。

   優點:(1) 不指定一塊記憶體大小的陣列的連續儲存,即可以像陣列一樣操作,但可以對此陣列進行動態操作。通常體現在push_back() pop_back()

         (2) 隨機訪問方便,即支援[ ]操作符和vector.at()

         (3) 節省空間。

   缺點:(1) 在內部進行插入刪除操作效率低。

         (2) 只能在vector的最後進行push和pop,不能在vector的頭進行push和pop。

         (3) 當動態新增的資料超過vector預設分配的大小時要進行整體的重新分配、拷貝與釋放

(2)list 雙向連結串列

    每一個結點都包括一個資訊快Info、一個前驅指標Pre、一個後驅指標Post。可以不分配必須的記憶體大小方便的進行新增和刪除操作。使用的是非連續的記憶體空間進行儲存。

   優點:(1) 不使用連續記憶體完成動態操作。

        (2) 在內部方便的進行插入和刪除操作

        (3) 可在兩端進行push、pop

   缺點:(1) 不能進行內部的隨機訪問,即不支援[ ]操作符和vector.at()

         (2) 相對於verctor佔用記憶體多

(3) deque   雙端佇列 double-end queue

   deque是在功能上合併了vector和list。

   優點:(1) 隨機訪問方便,即支援[ ]操作符和vector.at()

         (2) 在內部方便的進行插入和刪除操作

         (3) 可在兩端進行push、pop

   缺點:(1) 佔用記憶體多

使用區別:

     1)如果你需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector

     2)如果你需要大量的插入和刪除,而不關心隨機存取,則應使用list

     3)如果你需要隨機存取,而且關心兩端資料的插入和刪除,則應使用deque


三者的比較

下圖描述了vector 、list 、deque 在記憶體結構上的特點:

vector 是一段連續的記憶體塊,而deque 是多個連續的記憶體塊, list 是所有資料元素分開儲存,可以是任何兩個元素沒有連續。

vector 的查詢效能最好,並且在末端增加資料也很好,除非它重新申請記憶體段;適合高效地隨機儲存。

list 是一個連結串列,任何一個元素都可以是不連續的,但它都有兩個指向上一元素和下一元素的指標。所以它對插入、刪除元素效能是最好的,而查詢效能非常差;適合 大量地插入和刪除操作而不關心隨機存取的需求。

deque 是介於兩者之間,它兼顧了陣列和連結串列的優點,它是分塊的連結串列和多個陣列的聯合。所以它有被list 好的查詢效能,有被vector 好的插入、刪除效能。 如果你需要隨即存取又關心兩端資料的插入和刪除,那麼deque 是最佳之選。

關聯容器

set, multiset, map, multimap 是一種非線性的樹結構,具體的說採用的是一種比較高效的特殊的平衡檢索二叉樹—— 紅黑樹結構。(至於什麼是紅黑樹,我也不太理解,只能理解到它是一種二叉樹結構)

因為關聯容器的這四種容器類都使用同一原理,所以他們核心的演算法是一致的,但是它們在應用上又有一些差別,先描述一下它們之間的差別。

set ,又稱集合,實際上就是一組元素的集合,但其中所包含的元素的值是唯一的,且是按一定順序排列的,集合中的每個元素被稱作集合中的例項。因為其內部是通過連結串列的方式來組織,所以在插入的時候比vector 快,但在查詢和末尾新增上被vector 慢。

multiset ,是多重集合,其實現方式和set 是相似的,只是它不要求集合中的元素是唯一的,也就是說集合中的同一個元素可以出現多次。

map ,提供一種“鍵- 值”關係的一對一的資料儲存能力。其“鍵”在容器中不可重複,且按一定順序排列(其實我們可以將set 也看成是一種鍵- 值關係的儲存,只是它只有鍵沒有值。它是map 的一種特殊形式)。由於其是按連結串列的方式儲存,它也繼承了連結串列的優缺點。

multimap , 和map 的原理基本相似,它允許“鍵”在容器中可以不唯一。

關聯容器的特點是明顯的,相對於順序容器,有以下幾個主要特點:

1, 其內部實現是採用非線性的二叉樹結構,具體的說是紅黑樹的結構原理實現的;

2, set 和map 保證了元素的唯一性,mulset 和mulmap 擴充套件了這一屬性,可以允許元素不唯一;

3, 元素是有序的集合,預設在插入的時候按升序排列。

基於以上特點,

1, 關聯容器對元素的插入和刪除操作比vector 要快,因為vector 是順序儲存,而關聯容器是鏈式儲存;比list 要慢,是因為即使它們同是鏈式結構,但list 是線性的,而關聯容器是二叉樹結構,其改變一個元素涉及到其它元素的變動比list 要多,並且它是排序的,每次插入和刪除都需要對元素重新排序;

2, 關聯容器對元素的檢索操作比vector 慢,但是比list 要快很多。vector 是順序的連續儲存,當然是比不上的,但相對鏈式的list 要快很多是因為list 是逐個搜尋,它搜尋的時間是跟容器的大小成正比,而關聯容器 查詢的複雜度基本是Log(N) ,比如如果有1000 個記錄,最多查詢10 次,1,000,000 個記錄,最多查詢20 次。容器越大,關聯容器相對list 的優越性就越能體現;

3, 在使用上set 區別於vector,deque,list 的最大特點就是set 是內部排序的,這在查詢上雖然遜色於vector ,但是卻大大的強於list 。

4, 在使用上map 的功能是不可取代的,它儲存了“鍵- 值”關係的資料,而這種鍵值關係採用了類陣列的方式。陣列是用數字型別的下標來索引元素的位置,而map 是用字元型關鍵字來索引元素的位置。在使用上map 也提供了一種類陣列操作的方式,即它可以通過下標來檢索資料,這是其他容器做不到的,當然也包括set 。(STL 中只有vector 和map 可以通過類陣列的方式操作元素,即如同ele[1] 方式)

容器介面卡

STL 中包含三種介面卡:棧stack 、佇列queue 和優先順序priority_queue 。

介面卡是容器的介面,它本身不能直接儲存元素,它儲存元素的機制是呼叫另一種順序容器去實現,即可以把介面卡看作“它儲存一個容器,這個容器再儲存所有元素”。

STL 中提供的三種介面卡可以由某一種順序容器去實現。預設下stack 和queue 基於deque 容器實現,priority_queue 則基於vector 容器實現。當然在建立一個介面卡時也可以指定具體的實現容器,建立介面卡時在第二個引數上指定具體的順序容器可以覆蓋介面卡的預設實現。

由於介面卡的特點,一個介面卡不是可以由任一個順序容器都可以實現的。

棧stack 的特點是後進先出,所以它關聯的基本容器可以是任意一種順序容器,因為這些容器型別結構都可以提供棧的操作有求,它們都提供了push_back 、pop_back 和back 操作;

佇列queue 的特點是先進先出,介面卡要求其關聯的基礎容器必須提供pop_front 操作,因此其不能建立在vector 容器上;

優先順序佇列priority_queue 介面卡要求提供隨機訪問功能,因此不能建立在list 容器上。

相關文章