STL 容器詳解,stl容器詳解
STL的容器可以分為以下幾個大類:
一:序列容器, 有vector, list, deque, string.
二 : 關聯容器, 有set, multiset, map, mulmap, hash_set, hash_map, hash_multiset, hash_multimap
三: 其他的雜項: stack, queue, valarray, bitset
容器類共用部分公用介面。標準庫定義的三種順序容器類型:vector、list、deque(double-ended queue的縮寫,發音為“deck”),它們的差別僅在訪問元素的方式,以及添加或刪除元素相關操作的代價。順序容器適配器包括:stack、queue和priority_queue。容器只定義了少量操作,大多數操作由演算法庫提供。如果兩個容器提供了相同的操作,則它們的介面(函數名和參數個數)應該相同。
| 標準容器類 |
說明 |
| 順序性容器 |
| vector |
從後面快速的插入與刪除,直接存取任何元素 |
| deque |
從前面或後面快速的插入與刪除,直接存取任何元素 |
| list |
雙鏈表,從任何地方快速插入與刪除 |
| 關聯容器 |
| set |
快速尋找,不允許重複值 |
| multiset |
快速尋找,允許重複值 |
| map |
一對多映射,基於關鍵字快速尋找,不允許重複值 |
| multimap |
一對多映射,基於關鍵字快速尋找,允許重複值 |
| 容器適配器 |
| stack |
後進先出 |
| queue |
先進先出 |
| priority_queue |
最高優先順序元素總是第一個出列 |
容器類型
| vector |
容器,支援快速隨機訪問(連續儲存) |
| list |
鏈表,支援快速插入/刪除 |
| deque |
雙端隊列,支援隨機訪問(連續儲存),兩端能快速插入和刪除 |
| stack |
棧 |
| queue |
隊列 |
| priority_queue |
優先順序隊列 |
順序容器的定義順序容器的構造和初始化下表為迭代器為所有容器類型所提供的運算:
| *iter |
傳回型別iter所指向的元素的引用 |
| iter->mem |
對iter進行解引用,並取得指定成員 |
| ++iter |
給iter加1,使其指向容器中下一個元素 |
| iter++ |
|
| --iter |
給iter減1,使其指向容器中前一個元素 |
| iter-- |
|
| iter1 == iter2 |
當兩個迭代器指向同一個容器中的同一元素,或者當它們都指向 |
| iter1 != iter2 |
同一個容器的超出末端的下一個位置時,兩個迭代器相等。 |
vector和deque容器的迭代器提供了額外的運算:迭代器的算術運算和另一些關係運算,如下表所示:
| iter + n |
在迭代器上加(減)整數值,將產生指向容器中前面(後面)第n個元素的迭代器; |
| iter - n |
新計算出來的迭代器必須指向容器中的元素或超出容器末端的下一位置。 |
| iter1 += iter2 |
複合運算:先加(減),再賦值 |
| iter1 -= iter2 |
|
| iter1 - iter2 |
只適用於vector和deque |
| >, >=, <, <= |
比較迭代器的位置關係;只適用於vector和deque |
關係操作符只適用於vector和deque容器,這是因為只有這兩種容器為其元素提供快速、隨機的訪問。它們確保可根據元素位置直接有效地訪問指定的容器元素。這兩種容器都支援通過元素位置實現的隨機訪問,因此它們的迭代器可以有效地實現算術和關係運算。迭代器範圍:[first, last)是一個左閉合區間,表示範圍從first開始,到last結束,但不包括last。注意:如果first不等於last,則對first反覆做自增運算必須能夠到達last;否則,即last位於first之前,則將發生未定義行為。 迭代器範圍使用左閉合的意義:因為這樣可以統一表示空集,就無需特別處理。 另外,使用迭代器時,要特別留意迭代器的可能的失效問題。訪問元素
| back() |
返回容器的最後一個元素的引用。如果容器為空白,則該操作未定義 |
| front() |
返回容器的第一個元素的引用。如果容器為空白,則該操作未定義 |
| c[n] |
返回下標為n的元素的引用;如果n<0 or n>=size(),則該操作未定義 (註:只適用於vector和deque容器) |
| at[n] |
返回下標為n的元素的引用;如果下標無效,則拋出異常out_of_range異常 (註:只適用於vector和deque容器) |
刪除元素
| erase(p) |
刪除迭代器p所指向的元素。返回一個迭代器,它指向被刪除的元素後面的元素。如果p指向容器內最後一個元素,則返回的迭代器指向容器的超出末端的下一個位置;如果p本身就是指向超出末端的下一個位置的迭代器,則該函數未定義 |
| erase(b, e) |
刪除[b, e)內的所有元素。返回一個迭代器,它指向被刪除元素段後面的元素。如果e本身就是指向超出末端的下一個位置的迭代器,則返回的迭代器也指向超出末端的下一個位置。 |
| clear() |
刪除容器內的所有元素,返回void |
| pop_back() |
刪除容器內的最後一個元素,返回void。如果容器為空白,則該操作未定義。 |
| pop_front() |
刪除容器內的第一個元素,返回void。如果c為空白容器,則該操作未定義 (註:只適用於list和deque容器) |
賦值與swap
| c1 = c2 |
刪除容器c1的所有元素,然後將c2的元素複製給c1。c1和c2的類型必須相同。 |
| c1.swap(c2) |
交換內容:調用該函數後,c1中存放的是c2原來的元素,c2中存放的是c1原來的元素。c1和c2的類型必須相同。該函數的執行速度通常要比將c2的元素複製到c1的操作快。 |
| c.assign(b, e) |
重新設定c的元素:將迭代器b和e標記的範圍內所有的元素複製到c中。b和e必須不是指向c中元素的迭代器。 |
| c.assign(n, t) |
將容器c重新設定為儲存n個值為t的元素。 |
注意:assign操作首先刪除容器內所有的元素,再將參數所指定的新元素插入到容器中。 swap操作不會刪除或插入任何元素,而且保證在常量時間內實現交換。由於容器內沒有移動任何元素,因此迭代器不會失效。但要注意這些迭代器指向了另一個容器中的元素。
容器的選用:vector和deque容器提供了對元素的快速存取,但付出的代價是,在容器的任意位置插入或刪除元素,比在容器尾部插入和刪除的開銷更大,因為要保證其連續儲存,需要移動元素;list類型在任何位置都能快速插入和刪除,因為不需要保證連續儲存,但付出的代價是元素的隨機訪問開銷較大。特徵如下: 1)與vector容器一樣,在deque容器的中間insert或erase元素效率比較低; 2)不同於vector容器,deque容器提供高效地在其首部實現insert和erase的操作,就像在尾部一樣; 3)與vector容器一樣而不同於list容器的是,deque容器支援對所有元素的隨機訪問。 4)在deque容器首部或尾部刪除元素則只會使指向被刪除元素的迭代器失效。在deque容器的任何其他位置的插入和刪除操作將使指向該容器元素的所有迭代器都失效。
容器的比較:
vector (連續的空間儲存,可以使用[]操作符)快速的訪問隨機的元素,快速的在末尾插入元素,但是在序列中間歲間的插入,刪除元素要慢,而且如果一開始分配的空間不夠的話,有一個重新分配更大空間,然後拷貝的效能開銷。
deque (小片的連續,小片間用鏈表相連,實際上內部有一個map的指標,因為知道類型,所以還是可以使用[],只是速度沒有vector快)快速的訪問隨機的元素,快速的在開始和末尾插入元素,隨機的插入,刪除元素要慢,空間的重新分配要比vector快,重新分配空間後,原有的元素不需要拷貝。對deque的排序操作,可將deque先複製到vector,排序後在複製回deque。
list (每個元素間用鏈表相連)訪問隨機元素不如vector快,隨機的插入元素比vector快,對每個元素分配空間,所以不存在空間不夠,重新分配的情況。
set:內部元素唯一,用一棵平衡樹結構來儲存,因此遍曆的時候就排序了,尋找也比較快的哦。
map :一對一的映射的結合,key不能重複。
stack :適配器,必須結合其他的容器使用,stl中預設的內部容器是deque。先進後出,只有一個出口,不允許遍曆。
queue: 是受限制的deque,內部容器一般使用list較簡單。先進先出,不允許遍曆。
vector<bool> 與bitset<> ,前面的可以動態改變長度。
priority_queue: 插入的元素就有優先順序順序,top出來的就是優先順序最高的了
valarray 專門進行數值計算的,增加特殊的數學函數。
一些容器選用法則: 1)
如果程式要求隨機訪問元素,則應使用vector或deque容器; 2)
如果程式必須在容器的中間位置插入或刪除元素,則應採用list容器; 3)如果程式不是在容器的中間位置,而是在容器首部或尾部插入或刪除元素,則應採用deque容器; 4)如果只需要在讀取輸入時在容器的中間位置插入元素,然後需要隨機訪問元素,則可以在輸入時將元素讀入到一個list容器中,然後對容器排序,再將排序後的list容器複製到vector容器中。 5)如果程式既需要隨機訪問,又需要在容器的中間位置插入或刪除元素,此時應當權衡哪種操作的影響較大,從而決定選擇list容器還是vector或deque容器。註:此時若選擇使用vector或deque容器,可以考慮只使用它們和list容器所共有的操作,比如使用迭代器而不是下標,避免隨機訪問元素等,這樣在必要時,可以很方便地將程式改寫為使用list容器。容器適配器 適配器(adaptor)是標準庫中通用的概念,包括容器適配器、迭代器適配器和函數適配器。本質上,適配器是使一事物的行為類似於另一事物的行為的一種機制。容器適配器讓一種已存在的容器類型採用另一種不同的抽象類別型的工作方式實現,只是發生了介面轉換而已。 標準庫提供了三種順序容器適配器:queue, priority_queue和stack。 所有適配器都定義了兩個建構函式:預設建構函式用於建立Null 物件,而帶一個容器參數的建構函式將參數容器的副本作為其基礎值。 預設的stack和queue都基於deque容器實現,而priority_queue則在vector容器上實現。在建立適配器時,通過將一個順序容器指定為適配器的第二個型別參數,可覆蓋其關聯的基礎容器類型。例如: stack<int, vector<int> > int_stack; // 此時,int-stack棧是基於vector實現 對於給定的適配器,其關聯的容器必須滿足一定的約束條件。stack適配器所關聯的基本容器可以是任意一種順序容器類型,因為這些容器類型都提供了push_back、pop_back和back操作;queue適配器要求其關聯的基礎容器必須提供pop_front操作,因此其不能建立在vector容器上;priority_queue適配器要求提供隨機訪問功能,因此不能建立在list容器上。 兩個相同類型的適配器可以做==, !=, <, >, <=, >=這些關係運算,只要其基本元素類型支援==和<兩個操作即可。這與容器大小比較原則一致。這與容器大小比較原則一致。棧
| s.empty() |
如果棧為這人,則true;否則返回false |
| s.size() |
返回棧中元素的個數 |
| s.pop() |
刪除棧頂元素,但不返回其值 |
| s.top() |
返回棧頂元素的值,但不刪除該元素 |
| s.push(item) |
在棧項壓入新元素 |
隊列和優先順序隊列 標準庫隊列使用了先進先出(FIFO)的儲存和檢索策略,進入隊列的元素被放置在尾部,下一個被取出的元素則取自隊列的首部。 priority_queue預設使用元素類型的 < 操作符來確定它們之間的優先順序關係,使用者也可以定義自己的優先順序關係。在優先順序隊列中,新元素被放置在比它優先順序低的元素的前面。
| q.empty() |
如果隊列為空白,則返回true;否則返回false |
| q.size() |
返回隊列中元素的個數 |
| q.pop() |
刪除隊首元素,但不返回其值 |
| q.front() |
返回隊首元素的值,但不刪除該元素 (註:該操作只適用於隊列) |
| q.back() |
返回隊尾元素的值,但不刪除該元素 (註:該操作只適用於隊列) |
| q.top() |
返回具有最高優先順序的元素值,但不刪除該元素 (註:該操作只適用於優先順序隊列。MSDN也為queue提供了該操作) |
| q.push(item) |
對於queue,在隊尾壓入一個新元素; 對於priority_queue,在基於優先順序的適當位置插入新元素 |
STL各個容器的實現:
(1) vector
內部資料結構:數組。
隨機訪問每個元素,所需要的時間為常量。
在末尾增加或刪除元素所需時間與元素數目無關,在中間或開頭增加或刪除元素所需時間隨元素數目呈線性變化。
可動態增加或減少元素,記憶體管理自動完成,但程式員可以使用reserve()成員函數來管理記憶體。
vector的迭代器在記憶體重新分配時將失效(它所指向的元素在該操作的前後不再相同)。當把超過capacity()-size()個元素插入vector中時,記憶體會重新分配,所有的迭代器都將失效;否則,指向當前元素以後的任何元素的迭代器都將失效。當刪除元素時,指向被刪除元素以後的任何元素的迭代器都將失效。
(2)deque
內部資料結構:數組。
隨機訪問每個元素,所需要的時間為常量。
在開頭和末尾增加元素所需時間與元素數目無關,在中間增加或刪除元素所需時間隨元素數目呈線性變化。
可動態增加或減少元素,記憶體管理自動完成,不提供用於記憶體管理的成員函數。
增加任何元素都將使deque的迭代器失效。在deque的中間刪除元素將使迭代器失效。在deque的頭或尾刪除元素時,只有指向該元素的迭代器失效。
(3)list
內部資料結構:雙向環狀鏈表。
不能隨機訪問一個元素。
可雙向遍曆。
在開頭、末尾和中間任何地方增加或刪除元素所需時間都為常量。
可動態增加或減少元素,記憶體管理自動完成。
增加任何元素都不會使迭代器失效。刪除元素時,除了指向當前被刪除元素的迭代器外,其它迭代器都不會失效。
(4)slist
內部資料結構:單向鏈表。
不可雙向遍曆,只能從前到後地遍曆。
其它的特性同list相似。
(5)stack
適配器,它可以將任意類型的序列容器轉換為一個堆棧,一般使用deque作為支援的序列容器。
元素只能後進先出(LIFO)。
不能遍曆整個stack。
(6)queue
適配器,它可以將任意類型的序列容器轉換為一個隊列,一般使用deque作為支援的序列容器。
元素只能先進先出(FIFO)。
不能遍曆整個queue。
(7)priority_queue
適配器,它可以將任意類型的序列容器轉換為一個優先順序隊列,一般使用vector作為底層儲存方式。
只能訪問第一個元素,不能遍曆整個priority_queue。
第一個元素始終是優先順序最高的一個元素。
(8)set
鍵和值相等。
鍵唯一。
元素預設按升序排列。
如果迭代器所指向的元素被刪除,則該迭代器失效。其它任何增加、刪除元素的操作都不會使迭代器失效。
(9)multiset
鍵可以不唯一。
其它特點與set相同。
(10)hash_set
與set相比較,它裡面的元素不一定是經過排序的,而是按照所用的hash函數指派的,它能提供更快的搜尋速度(當然跟hash函數有關)。
其它特點與set相同。
(11)hash_multiset
鍵可以不唯一。
其它特點與hash_set相同。
(12)map
鍵唯一。
元素預設按鍵的升序排列。
如果迭代器所指向的元素被刪除,則該迭代器失效。其它任何增加、刪除元素的操作都不會使迭代器失效。
(13)multimap
鍵可以不唯一。
其它特點與map相同。
(14)hash_map
與map相比較,它裡面的元素不一定是按鍵值排序的,而是按照所用的hash函數指派的,它能提供更快的搜尋速度(當然也跟hash函數有關)。
其它特點與map相同。
(15)hash_multimap
鍵可以不唯一。
其它特點與hash_map相同。
C++ STL容器的選擇
一些常見的容器選擇問題
? 你是否需要在容器的任意位置插入新元素?
如果需要,就選擇序列容器;關聯容器是不行的。
? 你是否關心容器中的元素是如何排序的?
如果不關心,則雜湊容器是一個可行的選擇方案;否則,你要避免雜湊容器。
? 你選擇的容器必須是標準C++的一部分嗎?
如果必須是,就排除了雜湊容器、slist和rope。
? 你需要哪種類型的迭代器?
如果它們必須是隨機訪問迭代器,則對容器的選擇就被限定為vector、deque和string。或許你也可以考慮rope。如果要求使用雙向迭代器,那麼你必須避免slist以及雜湊容器的一個常見實現。
? 當發生元素的插入或刪除操作時,避免移動容器中原來的元素是否很重要?
如果是,就要避免連續記憶體的容器。
? 容器中的資料的布局是否需要和C相容?
如果需要相容,就只能選擇vector容器。
? 元素的尋找速度是否關鍵的考慮因素?
如果是,就要考慮雜湊容器,排序的vector,和標準關聯容器 --- 或許這就是優先順序。
? 如果容器內部使用了引用計數技術(reference counting),你是否介意?
如果是,就要避免使用string,因為許多string的實現都是用了引用計數。Rope也需要避免,因為權威的rope實現是基於引用計數的。當然,你需要某種表示字串的方法,可以考慮vector<char>。
? 對插入和刪除操作,你需要事務語義(transactional semantics)嗎?也就是說在插入和刪除操作失敗時,你需要復原的能力嗎?
如果需要,你就要使用基於節點的容器。如果對多個元素的插入操作需要事務語義,則你需要選擇list,因為在標準容器中,只有list對多個元素的插入操作提供了事務語義。
? 你需要使迭代器、指標和引用變為無效的次數最少嗎?
如果是這樣,就要使用基於節點的容器,因為對這類容器的插入和刪除操作從來不會使迭代器、指標和引用變為無效(除非它們指向了一個你正在刪除的元素)。而針對連續記憶體容器的插入和刪除操作一般會使指向該容器的迭代器、指標和引用變為無效。
? 如果序列容器的迭代器是隨機訪問迭代器類型,而且只要沒有刪除操作發生,且插入操作只發生在容器的末尾,則指向資料的指標和引用就不會變為無效,這樣的容器是否對你有協助?
這是非常特殊的情況,但如果你面對的情形正是如此,則deque是你所希望的容器。(有意思的是,當插入操作僅在容器末尾發生時,deque的迭代器有可能變為無效。deque是唯一的迭代器可能會變為無效而指標和引用不會變為無效的STL標準容器。)