C++的STL模板庫中提供了3種容器類:vector,list,deque
對於這三種容器,在覺得好用的同時,經常會讓我們困惑應該選擇哪一種來實現我們的邏輯。
在少量數據操作的程序中隨便哪一種用起來感覺差別並不是很大,
但是當數據達到一定數量后,會明顯感覺性能上有很大差異。
本文就試圖從介紹,以及性能比較兩個方面來討論這個問題。
vector - 會自動增長的數組
vector又稱為向量數組,他是為了解決程序中定義的數組是
不能動態改變大小這個缺點而出現的。
一般程序實現是在類創建的時候同時創建一個定長數組,
隨着數據不斷被寫入,一旦數組被填滿,則重新開辟一塊更大的內存區,
把原有的數據復制到新的內存區,拋棄原有的內存,如此反復。
由於程序自動管理數組的增長,對於我們程序員來說確實輕松了不少,
只管把數據往里面插就行了,當然把物理內存和虛擬內存插爆掉了
就是操作系統來找你麻煩了:-)
vector由於數組的增長只能向前,所以也只提供了后端插入和后端刪除,
也就是push_back和pop_back。當然在前端和中間要操作數據也是可以的,
用insert和erase,但是前端和中間對數據進行操作必然會引起數據塊的移動,
這對性能影響是非常大的。
對於所有數組來說,最大的優勢就是隨機訪問的能力。
在vector中,提供了at和[]運算符這兩個方法來進行隨機訪問。
由於每個數據大小相同,並且無間隔地排列在內存中,
所以要對某一個數據操作,只需要用一個表達式就能直接計算出地址:
address = base + index * datasize
同樣,對vector進行內存開辟,初始化,清除都是不需要花大力氣的,
從頭到尾都只有一塊內存。
list - 擅長插入刪除的鏈表
有黑必有白,世界萬物都是成對出現的。
鏈表對於數組來說就是相反的存在。
數組本身是沒有動態增長能力的(程序中也必須重新開辟內存來實現),
而鏈表強悍的就是動態增長和刪除的能力。
但對於數組強悍的隨機訪問能力來說的話,鏈表卻很弱。
list是一個雙向鏈表的實現。
為了提供雙向遍歷的能力,list要比一般的數據單元多出兩個指向前后的指針。
這也是沒辦法的,畢竟現在的PC內存結構就是一個大數組,
鏈表要在不同的環境中實現自己的功能就需要花更多空間。
list提供了push_back,push_front,pop_back,pop_front四個方法
來方便操作list的兩端數據的增加和刪除,不過少了vector的at和[]運算符的
隨機訪問數據的方法。並不是不能實現,而是list的設計者
並不想讓list去做那些事情,因為他們會做得非常差勁。
對於list來說,清除容器內所有的元素是一件苦力活,
因為所有數據單元的內存都不連續,list只有一個一個遍歷來刪除。
deque - 擁有vector和list兩者優點的雙端隊列
黑與白,處於這兩個極端之間的就是令人愉悅的彩色了。
deque作為vector和list的結合體,確實有着不凡的實力。
STL的deque的實現沒有怎么去看過,不過根據我自己的猜測,
應該是把數組分段化,在分段的數組上添加指針來把所有段連在一起,
最終成為一個大的數組。
deque和list一樣,提供了push_back,push_front,
pop_back,pop_front四個方法。可以想象,如果要對deque的兩端進行操作,
也就是要對第一段和最后一段的定長數組進行重新分配內存區,
由於分過段的數組很小,重新分配的開銷也就不會很大。
deque也和vector一樣,提供了at和[]運算符的方法。
要計算出某個數據的地址的話,雖然要比vector麻煩一點,
但效率要比list高多了。
首先和list一樣進行遍歷,每次遍歷的時候累積每段數組的大小,
當遍歷到某個段,而且baseN <= index < baseN + baseN_length的時候,
通過address = baseN + baseN_index就能計算出地址
由於分過段的后鏈表的長度也不是很長,所以遍歷對於
整體性能的影響就微乎其微了。
看起來deque很無敵吧,不過deque和希臘神話的阿吉里斯一樣,
再怎么強大也是有自己的弱點的,之后的測試數據中就能看到了。
P.S.請搜索「阿吉里斯的腳后跟」來獲取詳細內容
