STL源碼分析:Containers

STL容器分兩種:序列式容器,關聯式容器。算法

上圖之內縮方式來表達基層與衍生層的關係。數據庫

heap內含一個vector,priority-queue內含一個heap、stack和queue都含一個deque,set/map/multiset/multimap都內含一個RB-tree,hash_x都內含一個hastable。數據結構

 

序列式容器

所謂序列式容器,其中的元素均可序(ordered),但未必有序(sorted)。C++語言自己提供了一個序列式容器array。STL另外再提供 上列呈現的序列式容器。dom

 

vector函數

vector的數據安排以及操做方式與array很是類似。二者惟一差異在於空間的運用的靈活性上。array是靜態空間,設定要先定義空間。vector是動態空間,它的內部機制會自行擴充空間以容納新元素。vector人運用對於內存的合理利用與運用的靈活性有很大的幫助。tornado

vector的實現技術,關鍵在於其對大小的控制以及從新配置時的數據移動效率。這裏具體要看空間配置的策略。性能

vector的數據結構:線性連續空間。它以兩個迭代器start和finish分別指向配置得來的連續空間中目前已被使用的範圍,並以迭代器end_of_storage指向整塊連續空間(含備用空間)的尾端。爲了下降空間配置時的速度成本,vector實際配置的大小可能比客戶端需求量更大一些,以備未來可能的擴充。這即是容量(capacity)的觀念。spa

vector的迭代器:由於普通指針就能夠知足vector的全部必要條件,而vector支持隨機存取,因此vector提供的是Random Access Iterators。.net

 

list指針

list的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。list對於空間的運用有絕對的精準,一點也不浪費。並且對於任何位置的元素插入或無素移除,list永遠是常數時間。

list和vector的選擇最多視所元素的多寡、元素的構造複雜度、元素存取行爲的特性而定。

list的數據結構:從list的節點結構看,STL中的list是個雙向鏈表。

list的迭代器:所其操做上看,其迭代器是Bidirectional Itertors。

 

deque

vector是單向開口的連續線性空間,deque則是一種雙向開口的連續線性空間。

deque沒有容量觀念。不像vector那樣「因舊空間不足而從新配置一塊更大空間,而後複製元素,再釋放舊空間」。

deque的數據結構:array沒法成長,vector雖可成長,卻只能向尾端成長,並且是個假象。deque採用一塊「map」做緩衝區,利用這個map成長。

deque的迭代器:和vector類似,是Random Access Iterators。

 

stack與queue

這兩種都沒有迭代器,數據結構是用deque實現的(其實用list也能夠)。

 

heap

heap不歸屬於stl容器組件,它是priority queue的助手。priority queue容許用戶以任何次序將任何元素推入容器內,但取出時必定是從優先權最高(也就是數值最高)的元素開始取。binary max heap正是具備這樣的特性,適合做爲priority queue的底層機制。

heap底部容器:以vector表現的徹底二叉樹。

實現算法:最大堆。

 

priority_queue

priority_queue是一個擁有權值觀念的queue。

priority_queue沒有迭代器。

 

slist

這是個單向鏈表,因此迭代器變爲Forward Iterator。

 

 

關聯式容器

標準的STL關聯式容器分爲set(集合)和map(映射表)兩大類,以及這兩大類的衍生體mutiset(多鍵集合)和multimap(多鍵映射表)。這些容器的底層機制均以RB-tree(紅黑樹)完成。RB-tree也是一個獨立容器,但並不開放給外界使用。

此外,STL還提供了一個關聯式容器:hash_table(散列表),以及以此爲底層機制而完成的hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多鍵集合)、hash_multimap(散列多鍵映射表)。

 

所謂關聯式容器,觀念上相似關聯式數據庫(key-value)。當元素被插入到關聯式容器中時,容器內部結構(多是RB-tree,也多是hash-table)便依照其鍵值大小,以某種特定規則將這個元素放置於適當位置。

關聯式容器沒有所謂頭尾(只有最大元素與最小元素)。

通常而言,關聯式容器的內部結構是一個balanced binary tree,以便得到良好的搜尋效率。

 

set

set的特性是全部元素都會根據元素的鍵值自動被排序。set元素的鍵值就是實值,實值就是健值。set不容許兩個元素有相同的鍵值。

 

map

map的特性是,全部元素都會根據元素的鍵值自動被排序。map的全部元素都是pair。

 

multiset與multimap

特性以及用法和各自兄弟(set,map)徹底相同,惟一的差異在於它們容許鍵值重複。

 

hashtable

二叉搜索樹表現的構造在一個假設上:輸入數據有足夠的隨機性。而hash_table(散列表)的數據結構的操做表現是以統計爲基礎,不需仰賴輸入元素的隨機性。

 

hash_set、hash_map、hash_multiset、hash_multimap

都是以hash_table爲底層機制。但其實元素不能自動排序。

 

Hash與Map的區別

權衡三個因素: 查找速度, 數據量, 內存使用,可擴展性,有序性。
整體來講,hash查找速度會比RB樹快,並且查找速度基本和數據量大小無關,屬於常數級別;而RB樹的查找速度是log(n)級別。並不必定常數就比log(n) 小,hash還有hash函數的耗時,明白了吧,若是你考慮效率,特別是在元素達到必定數量級時,考慮考慮hash。但若你對內存使用特別嚴格, 但願程序儘量少消耗內存,那麼必定要當心,hash可能會讓你陷入尷尬,特別是當你的hash對象特別多時,你就更沒法控制了,並且 hash的構造速度較慢。

紅黑樹並不適應全部應用樹的領域。若是數據基本上是靜態的,那麼讓他們待在他們可以插入,而且不影響平衡的地方會具備更好的性能。若是數據徹底是靜態的,例如,作一個哈希表,性能可能會更好一些。

在實際的系統中,例如,須要使用動態規則的防火牆系統,使用紅黑樹而不是散列表被實踐證實具備更好的伸縮性。Linux內核在管理vm_area_struct時就是採用了紅黑樹來維護內存塊的。

紅黑樹是有序的,Hash是無序的,根據需求來選擇。

拿紅黑樹實現的Map和Hash實現的HashMap相比:

若是隻須要判斷Map中某個值是否存在之類的操做,固然是Hash實現的要更加高效。

若是是須要將兩個Map求並集交集差集等大量比較操做,就是紅黑樹實現的Map更加高效。

 

參考:

《STL源碼剖析》

RB-tree與Hashtable的區別與選擇

相關文章
相關標籤/搜索