map、multimap
map、multimap 是键-值对容器,根据键进行查找。
同样地,由于其内部结构类似二叉树,因此在插入元素时要进行排序;不能像 vector 使用其他元素替换给定位置的元素。
头文件:#include <map>
实例化
/*
实例化语法:
map <keyType, valueType, Predicate=std::less <keyType>> mapObj;
multimap <keyType, valueType, Predicate=std::less <keyType>> mmapObj;
第三个模板参数可选:默认是std::less<>用作排序标准
*/
#include <map>
#include <iostream>
#include <string>
using namespace std;
template <typename T>
struct reverseSort
{
bool operator()(const T& key1, const T& key2)
{
return key1 > key2;
}
};
int main()
{
// 初始化1:int -> string
map <int, string> mapIntToString1;
multimap <int, string> multimapIntToString1;
// 2. 以另一个 map/multimap 进行初始化
map <int, string> mapIntToString2(mapIntToString1);
multimap <int, string> multimapIntToString2(multimapIntToString2);
// 3. 以另一个 map/multimap 的一部分进行初始化
map <int, string> mapIntToString3(mapIntToString1.cbegin(),
mapIntToString1.cend());
multimap <int, string> multimapIntToString3(multimapIntToString1.cbegin(),
multimapIntToString1.cend());
// 4. 带谓词的初始化
map <int, string, reverseSort<int>> mapIntToString4
(mapIntToString1.cbegin(), mapIntToString1.cend());
// 用map实例化multimap
multimap <int, string, reverseSort<int>> multimapIntToString4
(mapIntToString1.cbegin(), mapIntToString1.cend());
system("pause");
return 0;
}
插入
/*
用成员函数 insert:
1. make_pair(key, value)
2. std::pair(key, value)
3. 类似数组的语法:mapIntToStr [1000000] = "One Million";
*/
#include <iostream>
#include <string>
#include <map>
using namespace std;
typedef map <int, string> MAP_INT_STRING;
typedef multimap <int, string> MMAP_INT_STRING;
template <typename T>
void DisplayElement (const T& input)
{
for (auto iElement = input.cbegin(); iElement != input.cend(); ++iElement)
{
cout << iElement->first << " -> " << iElement->second << endl;
}
cout << endl;
}
int main()
{
MAP_INT_STRING mapIntToString;
// value_type:stl容器盛装的数据的数据类型,这里就是<int, string>
mapIntToString.insert(MAP_INT_STRING::value_type(3, "Three"));
// make_pair (key, value)
mapIntToString.insert(make_pair(-1, "Minus one"));
// pair
mapIntToString.insert(pair <int, string> (1000, "One thousand"));
// 数组语法添加元素
mapIntToString[1000000] = "One million";
cout << "The map contains " << mapIntToString.size();
cout << " key-value pairs. They are: " << endl;
DisplayElement(mapIntToString);
// 以map实例化一个multimap
MMAP_INT_STRING mmapIntToString (mapIntToString.cbegin(),
mapIntToString.cend());
mmapIntToString.insert(make_pair(1000, "Thousand"));
cout << endl << "The multimap contains" << mmapIntToString.size();
cout << "The elements in the multimap are: " << endl;
DisplayElement(mmapIntToString);
cout << "The number of pairs in the mutimap with 1000 as their key: "
<< mmapIntToString.count(1000) << endl;
system("pause");
return 0;
}
查找
multimap::find() 总返回一个迭代器,如:
auto pairFound = mmapIntToStr.find(key); // C++11
mutimap <int, string>::const_iterator iPairFound = mapIntToString.find(key);
再次提醒:
使用该迭代器之前首先要检查,确保 find() 返回成功再使用它访问找到的值:
if (pairFound != mapIntToStr.end())
{
cout << "Key " << pairFound->first << " points to Value: ";
cout << pairFound->second << endl;
}
else
cout << "Sorry, pair with key " << key << " not in map" << endl;
关于成员 multimap::find() 的用法:
#include <map>
#include <string>
#include <iostream>
using namespace std;
template <typename T>
void DisplayContents (const T& input)
{
for (auto iElement = input.cbegin()
; iElement != input.cend()
; ++iElement)
{
cout << iElement->first << " points to "
<< iElement->second << endl;
}
cout << endl;
}
int main()
{
map <int, string> mapIntToString;
mapIntToString.insert(make_pair(3, "Three"));
mapIntToString.insert(make_pair(45, "Fifty five"));
mapIntToString.insert(make_pair(-1, "Minus one"));
mapIntToString.insert(make_pair(1000, "One Thousand"));
cout << "The map contains " << mapIntToString.size();
cout << " key-value pairs. They are:" << endl;
DisplayContents(mapIntToString);
cout << "Enter the key you wish to find: ";
int key = 0;
cin >> key;
auto iPairFound = mapIntToString.find(key);
if (iPairFound != mapIntToString.cend())
{
cout << "Key " << iPairFound->first << " points to Value: ";
cout << iPairFound->second << endl;
}
else
{
cout << "The pair with key " << key << " not in map" << endl;
}
system("pause");
return 0;
}
查找
multimap::count( ) 确定有多少个值与指定的键对应,再对迭代器递增来访问这些相邻的值:
auto pairFound = mmapIntToStr.find(key);
// Check if find() succeeded
if(pairFound != mmapIntToStr.end())
{
// Find the number of pairs that have the same supplied key
size_t numPairsInMap = mmapIntToStr.count(1000);
for(size_t counter = 0;
counter < numPairsInMap; // stay within bounds
++ counter )
{
cout << "Key: " << pairFound->first; // key
cout << ", Value [" << counter << "] = ";
cout << pairFound->second << endl; // value
++ pairFound; // ******递增迭代*******
}
}
else
cout << "Element not found in the multimap";
删除
/*
1. 将键作为参数,这将删除包含指定键的所有键-值对:
mapObject.erase (key);
2. 接受迭代器作为参数,并删除迭代器指向的元素:
mapObject.erase(element);
3. 使用迭代器指定边界,从而将指定范围内的所有元素都从 map 或 multimap 中删除:
mapObject.erase (lowerBound, upperBound);
*/
#include <iostream>
#include <map>
#include <string>
using namespace std;
template <typename T>
void DisplayContents (const T& input)
{
for (auto iElement = input.cbegin()
; iElement != input.cend()
; ++iElement)
{
cout << iElement->first << " points to "
<< iElement->second << endl;
}
cout << endl;
}
int main()
{
multimap <int, string> mmapIntToString;
mmapIntToString.insert(make_pair(3, "Three"));
mmapIntToString.insert(make_pair(45, "Forty five"));
mmapIntToString.insert(make_pair(-1, "Minus one"));
mmapIntToString.insert(make_pair(1000, "One thousand"));
mmapIntToString.insert(make_pair(-1, "Minus one(two)"));
mmapIntToString.insert(make_pair(1000, "One Thousand(two)"));
cout << "The multimap contains " << mmapIntToString.size();
cout << " key-value pairs. " << "They are: " << endl;
DisplayContents(mmapIntToString);
auto NumPairsErased = mmapIntToString.erase(-1);
cout << "Erased " << NumPairsErased << " pairs with -1 as key." << endl;
auto iPairLocator = mmapIntToString.find(45);
if(iPairLocator != mmapIntToString.cend())
{
mmapIntToString.erase(45);
cout << "Erased a pair with 45 as key using an iterator" << endl;
}
cout << "Erasing the range of pairs with 1000 as key." << endl;
mmapIntToString.erase(mmapIntToString.lower_bound(1000),
mmapIntToString.upper_bound(1000));
cout << "The multimap now contains " << mmapIntToString.size();
cout << " key-value pair(s)." << "They are:" << endl;
DisplayContents(mmapIntToString);
system("pause");
return 0;
}
关于lower_bound() 和 upper_bound() ?
自定义的谓词排序
要提供不同的排序标准,可编写一个二元谓词 ——> 实现了 operator( ) 的类或结构:
template<typename keyType>
struct Predicate
{
bool operator()(const keyType& key1, const keyType& key2)
{
// your sort priority logic here
}
};
// 声明map
map <keyType, valueType, Predicate> mapObject;
对于键类型为 std::string 的 map,默认排序谓词 std::less 导致根据 std::string 类定义的 < 运算符进行排序,因此区分大小写。一种解决方案是在实例化 map 时提供一个排序谓词,根据不区分大小写的比较结果返回 true 或 false,如下所示:
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
template <typename T>
void DisplayContents (const T& input)
{
for (auto iElement = input.cbegin()
; iElement != input.cend()
; ++iElement)
{
cout << iElement->first << " points to "
<< iElement->second << endl;
}
cout << endl;
}
struct IgnoreCase
//class IgnoreCase
{
//public:
bool operator()(const string& str1, const string& str2) const
{
string str1NoCase(str1), str2NoCase(str2);
transform(str1.begin(), str1.end(), str1NoCase.begin(), ::tolower);
transform(str2.begin(), str2.end(), str2NoCase.begin(), ::tolower);
return (str1NoCase < str2NoCase);
}
};
typedef map <string, string> DIRECTORY_WITHCASE;
typedef map <string, string, IgnoreCase> DIRECTORY_NOCASE;
int main()
{
DIRECTORY_NOCASE dirCaseInsensitive;
dirCaseInsensitive.insert(make_pair("John", "2345764"));
dirCaseInsensitive.insert(make_pair("JOHN", "2345764"));
dirCaseInsensitive.insert(make_pair("Sara", "42367236"));
dirCaseInsensitive.insert(make_pair("Jack", "32435348"));
cout << "Displaying contents of the case-insensitive map: " << endl;
DisplayContents(dirCaseInsensitive);
DIRECTORY_WITHCASE dirCaseSensitive(dirCaseInsensitive.begin(),
dirCaseInsensitive.end());
cout << "Displaying contents of the case-sensitive map: " << endl;
DisplayContents(dirCaseSensitive);
cout << "Please enter a name to search: " << endl << "> ";
string strNameInput;
cin >> strNameInput;
auto iPairInNoCaseDir = dirCaseInsensitive.find(strNameInput);
if (iPairInNoCaseDir != dirCaseInsensitive.cend())
{
cout << iPairInNoCaseDir->first << "'s number in the case-insensitive";
cout << " directory is: " << iPairInNoCaseDir->second << endl;
}
else
{
cout << strNameInput << "'s number not found ";
cout << "in the case-insensitive directory" << endl;
}
auto iPairInCaseSensDir = dirCaseSensitive.find(strNameInput);
if (iPairInCaseSensDir != dirCaseSensitive.cend())
{
cout << iPairInCaseSensDir->first << "'s number in the case-sensitive";
cout << " directory is: " << iPairInCaseSensDir->second << endl;
}
else
{
cout << strNameInput << "'s number was not found ";
cout << "in the case-sensitive directory" << endl;
}
system("pause");
return 0;
}
也可以将结构 PredIgnoreCase 声明为类,此时需要给 operator( ) 加上关键字 public。
std::map 的查找性能虽然比 vector、list 高,但其查找速度会随着元素的增加而降低,所需时间与 map 包含元素对数成正比。
unordered_map、unordered_multimap
此类是基于散列表。
平均插入和删除时间是固定的,查找元素的时间也是固定的。
头文件:#include <unordered_map>
调用 find() 时会根据键查找元素,需要调用以下函数根据键来计算索引:
Index = HashFunction(key, TableSize);
初始化、查找、插入等操作与map等相同。需要注意的是 unordered_map 包含包含一个散列函数,用于计算排列顺序:
unordered_map<int, string>::hasher hFn = umapIntToStr.hash_function();
size_t hashingVal = hFn(1000);
// 在元素数达到或接近桶数时,它将自动执行负载均衡:
// load_factor( ) 指出了 unordered_map 桶的填满程度
cout << "Load factor: " << umapIntToStr.load_factor() << endl;
// load_factor( ) 超过max_load_factor( )时,unordered_map 将重新组织以增加桶数,并重建散列表
cout << "Max load factor = " << umapIntToStr.max_load_factor() << endl;
// 桶数量
cout << "Max bucket count = " << umapIntToStr.bucket_count() << endl;
具体实例:
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;
template <typename T1, typename T2>
void DisplayUnorderedMap (unordered_map<T1, T2>& Input)
{
cout << "Number of pairs, size(): " << Input.size() << endl;
cout << "Bucket count: " << Input.bucket_count() << endl;
cout << "Load factor: " << Input.load_factor() << endl;
cout << "Max load factor: " << Input.max_load_factor() << endl;
for (auto iElement = Input.cbegin(); iElement != Input.cend(); ++iElement)
{
cout << iElement->first << " -> " << iElement->second << endl;
}
cout << endl;
}
int main()
{
unordered_map <int, string> umapIntToStr;
umapIntToStr.insert(make_pair(1, "One"));
umapIntToStr.insert(make_pair(45, "Forty five"));
umapIntToStr.insert(make_pair(1001, "Thousand one"));
umapIntToStr.insert(make_pair(-2, "Minus Two"));
umapIntToStr.insert(make_pair(-1000, "Minus One Thousand"));
umapIntToStr.insert(make_pair(100, "One Hundred"));
umapIntToStr.insert(make_pair(12, "Twelve"));
umapIntToStr.insert(make_pair(-100, "Minus One Hundred"));
DisplayUnorderedMap<int, string>(umapIntToStr);
cout << endl;
/*
Number of pairs, size(): 8
Bucket count: 17
Load factor: 0.470588
Max load factor: 1
-100 -> Minus One Hundred
12 -> Twelve
45 -> Forty five
-1000 -> Minus One Thousand
1 -> One
-2 -> Minus Two
1001 -> Thousand one
100 -> One Hundred
*/
cout << "Inserting one more element:" << endl;
umapIntToStr.insert(make_pair(300, "Three handred"));
DisplayUnorderedMap<int, string>(umapIntToStr);
cout << endl;
/*
Inserting one more element:
Number of pairs, size(): 9
Bucket count: 17
Load factor: 0.529412
Max load factor: 1
-100 -> Minus One Hundred
12 -> Twelve
300 -> Three handred
45 -> Forty five
-1000 -> Minus One Thousand
1 -> One
-2 -> Minus Two
1001 -> Thousand one
100 -> One Hundred
*/
cout << "Enter key to find for: ";
int key = 0;
cin >> key; // 300
auto iElementFound = umapIntToStr.find(key);
if (iElementFound != umapIntToStr.end())
{
cout << "Found key " << iElementFound->first << " points to value: ";
cout << iElementFound->second << endl;
}
else
{
cout << "Key has no corresponding value in unordered map!" << endl;
}
// Enter key to find for: Found key 300 points to value: Three handred
system("pause");
return 0;
}