c++primer——關聯容器的使用

TinnCHEN發表於2019-04-03

一、順序關聯容器

(一)、容器的種類

順序關聯容器有四種:
可以按照關鍵字是否重複分為兩類:
1、不重複:
map:儲存key-value
set:儲存key
2、可以重複:
multimap:儲存key-value
multiset:儲存key

(二)、關鍵字(key)的要求

1、關鍵字型別必須定義元素比較的方法,所提供的操作必須是嚴格弱序,即<=。

2、當我們想使用自定義的操作,必須在定義關聯容器型別是提供此操作的型別。尖括號內指出要定義哪種型別的容器,緊跟著自定義的操作型別。
尖括號中給出的型別僅僅是一個型別,當我們建立一個容器時,才會以建構函式的形式提供真正的比較操作。
eg:

//Sales_data
bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs){
     return lhs.isbn() < rhs.isbn();
}

multiset<Sales_data, decltype(compareIsbn) *> bookstore(compareIsbn);
//or
multiset<Sales_data,  bool(*) (const Sales_data &s1, const Sales_data &s2)> bookstore(compareIsbn);
//or
using compareType = bool(*)(const Sales_data& lhs, const Sales_data& rhs);
multiset<Sales_data, compareType*> bookstore(compareIsbn);

(三)、pair型別

定義在標頭檔案utility中。
類似容器,儲存兩個資料成員,用來生成特定型別的模板。可以對資料成員進行預設初始化,採用值初始化的方式。
pair的資料成員是public,分別命名為first,second。

pair(T1, T2) p; //宣告一個pair。
make_pair(v1,v2); // 返回一個用v1,v2初始化的pair。pair的型別從v1,v2推匯出來。

pair(T1,T2) p 宣告一個pair。
make_pair(v1,v2) 返回一個用v1,v2初始化的pair。pair的型別從v1,v2推匯出來。

(四)、關聯容器的操作

1、key_type 此容器型別的關鍵字型別
2、value_type 對於set,與key_type相同,對於map,為:pair<const key_type, mapped_type>
3、mapped_type 每個關鍵字關聯的型別;只適用於map

一、關聯容器迭代器

1、當解引用一個關聯容器迭代器時,可以得到一個型別為容器的value_type的值的引用。
對於map,first為一個const的關鍵字,second儲存成員值。set為一個const關鍵字。

2、通常不對關聯容器採取泛型演算法。因為關鍵字在迭代器解引用後返回的是const,無法修改與重排元素。因此,我們只可對關聯容器採取只讀演算法。但泛型演算法在這方面通常要對容器進行搜尋,關聯容器不能通過關鍵字快速查詢元素,因此不建議對關聯容器採用泛型演算法。
ps:最好採用關聯容器自帶的演算法。

二、新增元素

1、與其他容器的insert操作類似,map的操作注意傳入一對元素或者使用pair,make_pair。
2、檢測insert返回值,insert返回一個pair,first為一個迭代器,指向具有給定關鍵字的元素,second為一個bool值,本條是針對map和set來說。當關鍵字不重複插入成功時,bool返回一個true,當關鍵字已經存在時,返回一個false,insert什麼也不做。

三、刪除元素
c.erase(k);  //從c中刪除每個關鍵字為k的元素,返回一個size_type值,指出刪除原色的數量
c.erase(p);  //從c中刪除迭代器p指定的元素,返回一個指向配置後元素的迭代器。若p為尾元素,返回end()
c.erase(b, e);  //刪除迭代器對b和e所表示的範圍中的元素。返回e
四、訪問元素
c.find(k); //返回一個迭代器,指向第一個關鍵字為k的元素,若k不再其中返回end()
c.count(k); //返回關鍵字等於k的元素的數量。
c.lower_bound(k); //返回一個迭代器,指向第一個不小於k的元素
c.upper_bound(k); //返回一個迭代器,指向第一個關鍵字大於k的元素
c.equal_range(k); //返回一個迭代器pair,表示關鍵字等於k的元素的範圍。若k不存在,pair的兩個成員均為end()

1、當我們訪問一個元素但不想改變原有容器的話,我們應採用find來代替下標訪問。
2、可以用find配合count來訪為multimap和multiset中相同關鍵字下的元素,count返回值用來表示範圍
3、遍歷容器時,我們可以採用lower_bound與upper_bound來表示一個迭代器範圍。若容器中沒有這個元素,則這兩個返回相等的迭代器,指向一個不影響排序的關鍵字的插入位置。
4、也可以用equal_range來代替第三條。

五、map的下標操作

1、若在元素中不存在該元素,則會為map建立一個元素,並進行值初始化。
2、我們只可以對非const的map使用下標操作。
3、當對一個map進行下標操作時,會獲得一個mapped_type物件,與解引用迭代器返回的物件不同。

六、p381 練習11.14
#include<iostream>
#include<map>
#include<set>
#include<string>
#include<algorithm>
#include<vector>
#include<string>
#include<list>
#include<utility>

using namespace std;

pair<string, int> process(string &s,int &n) {
	if (!s.empty()) {
		return{ s,n };
	}
	else {
		return make_pair(s, n);
	}
}

int main() {
//家庭名字以及孩子名字以及生日
	map<string, vector<pair<string,int>>> family;
	pair<string, int> ChildAndBirth;
	//set<string> exclude = { "0" };
	string firstName, childName;
	int Birthday;
	ostream &os = cout;
	while ([&firstName, &os]() ->bool 
	{os << "please input your family name: "; return cin >> firstName && (firstName != "end"); }()) {
		while ([&childName, &Birthday, &os]()->bool 
		{os << "please input your childname and birthday: "; return (cin >> childName >> Birthday) && (childName != "end"); }()) {
			ChildAndBirth = process(childName, Birthday);
			family[firstName].push_back(ChildAndBirth);
		}
	}
	map<string, vector<pair<string,int>>> ::iterator it;
	for (it = family.begin(); it != family.end(); ++it) {
		cout << (*it).first << " ";
		vector<pair<string, int>> ::iterator it1 = it->second.begin();
		for (; it1 != it->second.end(); ++it1) {
			cout << (*it1).first << " " << (*it1).second << endl;
		}
	}
	
	return 0;
}

二、無序容器

unordered_map
unordered_set
unordered_multimap
unordered_multiset

1、使用hash函式和關鍵字型別==來阻止元素。
2、當關鍵字元素沒有明顯的序列關係或者維護序的代價非常高昂時,使用無序容器很有用
3、提供了與有序容器相同的操作
4、無序容器在儲存組織上為一組桶。每個桶儲存零個或多個元素。無序容器使用一個hash函式將元素對映到桶。

//桶介面
c.bucket_count() //正在使用的桶的數目
c.max_buck_count() //容器能容納得最多的桶的數量
c.bucket_size(n) //第n個桶中有多少個元素
c.bucket(k) //關鍵字為k的元素在哪個桶中
//桶迭代
local_iterator //可以用來訪問桶中元素的迭代器型別
const_local_iterator //桶迭代器的const版本
c.begin(n),c.end(n) //桶n的首元素迭代器和尾後迭代器
c.cbegin(n),c.cend(n) //與前兩個函式類似,但返回const_local_iterator
//雜湊策略
c.load_factor() //每個桶的平均元素數量,返回float值
c.max_load_factor() //c試圖維護桶的平均大小,返回float值。c會在需要時新增新的桶,使load_factor<=max_load_factor
c.rehash(n) //重新儲存,使bucket_count >= n且bucket_count > size/max_load_factor
c.reserve(n) //重新儲存,是的c可以儲存n個元素且不必rehash

5、標準庫為內建型別,提供了hash模板,還有一些標準庫型別,包括string和智慧指標定義了hash,我們可以直接定義關鍵字為內建型別、string、智慧指標的無序容器
6、不能直接定義關鍵字型別為自定義型別的無序容器。我們必須提供我們自己的hash模板版本。

相關文章