[C++ Primer Note10] 關聯容器
關聯容器和順序容器的 本質 區別在於:關聯容器中的元素是按關鍵字來儲存和訪問的,而順序容器是按它們在容器中的位置來順序儲存和訪問的。
- 標準庫提供8個關聯容器
按關鍵字有序儲存元素:
- map
- set
- multimap
- multiset
無序集合:
- unordered_map
- unordered_set
- unordered_multimap
- unordered_multiset
其中, multi 表示允許重複關鍵字。map和multimap定義在標頭檔案 map 中;set和multiset定義在 set 中;無需容器定義在標頭檔案 unordered_map 和 unordered_set 中。
- 一個經典的使用關聯容器的例子是 單詞計數程式 :
map<string,int> word_count; string tmp; while(cin>>tmp){ ++word_count[tmp]; } for(const auto &w:word_count){ cout<<w.first<<":"<<w.second<<endl; }
一旦讀取完所有輸入,範圍for語句就會遍歷map,當從map中提取一個元素時,會得到一個 pair 型別的物件,它是一個模板型別,儲存兩個名為 first 和 second 的公有資料成員,前者為 key ,後者為 value 。
- 上一個程式的一個合理拓展是: 忽略常見單詞 ,我們可以使用set儲存想忽略的單詞,只對不在集合中的單詞統計出現次數:
map<string,int> word_count; set<string> exclude={"The","But","And"}; string tmp; while(cin>>tmp){ if(exclude.find(tmp)==exclude.end()) ++word_count[tmp]; }
set的 find 成員返回一個迭代器。如果給定關鍵字在set中,迭代器指向該關鍵字。否則,find返回 尾後迭代器 。
- 對於有序容器,關鍵字型別必須定義 元素比較 的方法,預設情況下,標準庫使用關鍵字型別的 < 運算子來比較兩個關鍵字。當然也可以自定義比較規則:
multiset<Sales_data,decltype(compareIsbn)*> bookstore(compareIsbn);
需要提供比較操作型別(一種函式指標型別),以及在建構函式中傳入比較函式
- pair 標準庫型別,定義在標頭檔案 utility 中,一個pair儲存兩個資料成員。pair的預設建構函式對資料成員進行值初始化,我們可以這樣初始化pair:
pair<string,string> author{"james","kevin"}; pair<int,string> student(1,"kevin");
- 關聯容器額外定義了幾個 類型別名 :
- key_type:關鍵字型別
- mapped_type:每個關鍵字關聯的型別
- value_type:對於set,就是key_type;對於map,是pair型別
- 當解引用一個關聯容器迭代器時,我們會得到一個型別為容器的 value_type 的值得引用。
- set的迭代器是 const 的 ,無論是iterator還是const_iterator。而map的pair中的第一個成員也是 const 的。
- map和set型別都支援前述的 begin 和 end 操作,對於 有序關聯容器 來說,按照關鍵字 升序 遍歷。
- 我們通常不對關聯容器使用泛型演算法 ,關鍵字是 const 這一特性意味著不能將關聯容器傳遞給修改或重排容器元素的演算法。對於搜尋演算法而言,泛型演算法是 順序搜尋 ,而關聯容器內部定義的 find 成員根據關鍵字搜尋會快得多。
一般來說,僅在把關聯容器當作 源序列 或者 目的位置 的時候才使用泛型演算法。 - 新增元素 :
新增元素
// 插入一個元素到map中 word_count.insert({word,1}); word_count.insert(make_pair(word,1)); word_count.insert(pair<string,size_t>(word,1)); word_count.insert(map<string,size_t>::value_type(word,1));
- insert(或emplace)返回值依賴於容器型別和引數,對於 不包含重複關鍵字 的容器,新增單一元素的insert和emplace返回一個 pair ,first成員是一個指向給定關鍵字的 迭代器 (即使插入失敗),second成員是一個 bool ,如果插入之前key已經存在返回 false ,否則插入成功返回 true 。對於允許重複關鍵字的容器,insert操作僅僅返回一個指向新元素的迭代器, 沒有bool值 。
- 刪除元素 :
刪除元素
- map的下標操作 :
下標操作
下標運算子在關鍵字不存在的情況下會建立元素並插入到map 。 - 與迭代器不同,map的下標操作返回的是 mapped_type 而不是解引用迭代器得到的 value_type 。
- 查詢元素 :
查詢元素
- 新標準定義了4個 無序關聯容器 ,這些容器不是使用比較運算子(樹)來組織元素,而是使用 雜湊函式 和關鍵字型別的 == 運算子。在關鍵字型別的元素沒有明顯的 序關係 的情況下,無序容器非常有用。
無序容器無非就是 一組桶 ,除了提供與有序容器相同的操作之外,它還提供了一組管理桶的成員函式,這些成員函式允許我們查詢容器的狀態以及在必要時強制容器進行重組:
無序容器管理操作
- 預設情況下,無序容器使用關鍵字型別的 == 運算子來比較元素,它們還使用一個 hash<key_type> 型別的物件來生成每個元素的雜湊值。標準庫為內建型別(包括指標)提供了hash模板。還為一些標準庫型別,包括string和 智慧指標 型別定義了hash。因此,我們可以直接定義關鍵字是內建型別,string以及智慧指標的無序容器。
- 但是,我們不能直接定義關鍵字型別為 自定義型別 的無序容器,與容器不同,不能直接使用雜湊模板,而必須提供我們自己的hash模板版本。但我們可以使用 類似於為有序容器過載關鍵字型別的方法提供函式代替==運算子和雜湊值計算函式去實現同樣的效果。