哈希表(Hash Table)也叫做散列表,是一种通过将关键字与存储位置映射起来,利用关键字直接访问存储位置上的 value 的数据结构,使得元素查找的时间复杂度达到 O(1)。
映射函数被称为散列函数(hash 函数),存储数据的数组叫做散列表,即 Hash Table。
为什么使用哈希表呢?
答:哈希表可以为我们的查找带来便利,由于底层是基于数组的,所有优缺点与数组类似:
优点:查找速度快,时间复杂度 O(1);
缺点:插入数据与删除一般情况下 时间复杂度 O(1),最坏的情况下可能需要移动其他元素,时间复杂度O(n)。
合适的 hash 函数可以使关键字的寻址快速迅捷
一般的 hash 函数有:
hash算法原理详解_哈希算法-CSDN博客
1. 直接定址法(常用):
直接取关键字的线性函数得到的值作为地址:
Hash(Key) = a * Key + b
2. 除留取余法(常用):
关键字除以小于散列表长度的素数得到的值作为地址:
Hash(Key) = Key % mod
3. 平均取中法(了解):
对关键字取平方,将得到结果的中间几位作为存储地址
假设关键字为 5644,对它平方就是 31854736,抽取中间的 3 位 547 作为哈希地址。
4. 折叠法(了解)
折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址
5.随机数法(了解)
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即 Hash(Key) = random(Key)
,其中 random 为随机数函数。
C++那些事之彻底搞懂STL HashTable - 知乎 (zhihu.com)
底层是一个数组,数组中每个索引代表一个 bucket,元素实际存储在链表上,若是出现 哈希冲突,新元素定位在一个 已有元素的 bucket 上,则将新元素 append 到bucket 的链表上。
这里只列出了_Hashtable 的模板参数部分
template
class _Hashtable
模板参数含义:
参数 | 含义 |
_Key | 关联容器的键类型 |
_Value | 关联容器的值类型 |
_Alloc | 用于内存分配的分配类型 |
_ExtractKey | 从值中提取键的函数对象类型 |
_Equal | 判断键是否相等的函数对象类型 |
_H1 | 第一个 Hash 的函数对象类型,用于计算关键字的 hashcode |
_H2 | 第二个 Hash 的函数对象类型,用于计算 hashcode 对应的bucket 索引 |
_Hash | Hash 函数对象类型 |
_RehashPolicy | 重新哈希函数策略类型 |
_Traits | 特定容器特定类型 |
常见函数解析:
template
auto
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
find(const key_type& __k) const
-> const_iterator
{
__hash_code __code = this->_M_hash_code(__k); // 计算关键字 key 的 hash code
std::size_t __n = _M_bucket_index(__k, __code); // 通过 hash code 获取桶的索引 index
__node_type* __p = _M_find_node(__n, __k, __code); // 通过桶的index、key 、hash code获取到 链表上的 node ,若是返回为 nullptr 说明没找到,否则便找到了相应的 node ,并将 node 塞进 const_iterator 中,作为函数的返回参数
return __p ? const_iterator(__p) : end();
}
find 函数分为如下几步:
1) 计算关键字 key 得 hash code 值
2) 依据 关键字与 1) 中计算出的 hash code 值,获取桶的索引 index
3) 通过桶的 index、key 与 hash code 获取实际存储数据的 node 节点
4) 返回 包含有 node 节点的 迭代器
_H1 是 是计算 hash code 时用的函数对象,_H2 是 通过 hashcode 计算 桶的索引的函数对象。
源代码:
奥秘在于 struct _Hash_code_base
template
struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
_Default_ranged_hash, true>
...
{
...
__hash_code
_M_hash_code(const _Key& __k) const
{ return _M_h1()(__k); }
std::size_t
_M_bucket_index(const _Key&, __hash_code __c,
std::size_t __n) const
{ return _M_h2()(__c, __n); }
...
const _H1&
_M_h1() const { return __ebo_h1::_S_cget(*this); }
_H1&
_M_h1() { return __ebo_h1::_S_get(*this); }
const _H2&
_M_h2() const { return __ebo_h2::_S_cget(*this); }
_H2&
_M_h2() { return __ebo_h2::_S_get(*this); }
};
template
struct _Hashtable_base
: public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
_Traits::__hash_cached::value>,
....
{
.....
};
template
class _Hashtable
: public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
_H1, _H2, _Hash, _Traits>,
...
{
...
};
_Hashtable 继承于 __detail::_Hashtable_base, 而 __detail::_Hashtable_base 继承于 struct _Hash_code_base 。
struct _Hash_code_base 中
1. 函数 hash_code = _M_hash_code(key) 计算关键字 的 hash_code 使用的就是 _H1 函数对象
2. 函数 _M_bucket_index(key, hash_code) 计算 hash_code 对应桶的索引,这里使用的就是 _H2 函数对象
_Hash_table 中使用的链表节点是 下面的 __node_type ,其来源于 __detail::_Hash_node, 若是
__hash_cashed::value 为 true ,则会存储 hash_code ,为 false,则不会存储 hash_code ,如下代码有两个分别针对 true 于false 的偏特化版本
using __node_type = __detail::_Hash_node<_Value, __hash_cached::value>;
/**
* Primary template struct _Hash_node.
*/
template
struct _Hash_node;
/**
* Specialization for nodes with caches, struct _Hash_node.
*
* Base class is __detail::_Hash_node_value_base.
*/
template
struct _Hash_node<_Value, true> : _Hash_node_value_base<_Value>
{
std::size_t _M_hash_code;
_Hash_node*
_M_next() const noexcept
{ return static_cast<_Hash_node*>(this->_M_nxt); }
};
/**
* Specialization for nodes without caches, struct _Hash_node.
*
* Base class is __detail::_Hash_node_value_base.
*/
template
struct _Hash_node<_Value, false> : _Hash_node_value_base<_Value>
{
_Hash_node*
_M_next() const noexcept
{ return static_cast<_Hash_node*>(this->_M_nxt); }
};
#include
#include
int main()
{
std::_Hashtable, std::_Identity,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
std::__detail::_Hashtable_traits>::value, true, true>> hash_table;
for(int i = 0; i < 10; i++)
{
hash_table.insert(i+1);
std::cout << "element num: " << hash_table.size() << ", bucket count: " << hash_table.bucket_count() << std::endl;
}
hash_table.insert(1);
hash_table.insert(1);
hash_table.insert(1);
for(auto iter = hash_table.begin(); iter != hash_table.end(); iter++)
{
std::cout << *iter << " ";
}
std::cout << "" << std::endl;
std::cout << "-----------------------------" << std::endl;
std::_Hashtable, std::_Identity,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
std::__detail::_Hashtable_traits>::value, true, false>> multi_hash_table;
for(int i = 0; i < 10; i++)
{
multi_hash_table.insert(i+1);
std::cout << "element num: " << multi_hash_table.size() << ", bucket count: " << multi_hash_table.bucket_count() << std::endl;
}
multi_hash_table.insert(1);
multi_hash_table.insert(1);
multi_hash_table.insert(1);
for(auto iter = multi_hash_table.begin(); iter != multi_hash_table.end(); iter++)
{
std::cout << *iter << " ";
}
std::cout << "" << std::endl;
return 0;
}
输出:
示例中第一个 hash_table 中的元素不可重复,第二个 multi_hash_table 中的元素是可以重复的。
原因在于 模板参数中的最后一个参数 _Traits ,示例中用的 _Traits 对应的函数对象为 _Hashtable_traits ,源码如下:_unique_keys 为 true 时,hash table 的元素不可重复,若是为 false,则 hash table 的元素可以重复
template
struct _Hashtable_traits
{
using __hash_cached = __bool_constant<_Cache_hash_code>;
using __constant_iterators = __bool_constant<_Constant_iterators>;
using __unique_keys = __bool_constant<_Unique_keys>;
};
#include
#include
#include
int main()
{
// 1. key 唯一
std::_Hashtable, std::allocator, std::__detail::_Select1st,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
std::__detail::_Hashtable_traits>::value, true, true>> hash_table_map;
std::string s1 = "aaa";
hash_table_map.insert(std::make_pair(1, s1));
std::string s2 = "bbb";
hash_table_map.insert(std::make_pair(2, s2));
std::string s3 = "ccc";
hash_table_map.insert(std::make_pair(3, s3));
std::string s4 = "ddd";
hash_table_map.insert(std::make_pair(4, s4));
std::string s5 = "eee";
hash_table_map.insert(std::make_pair(5, s5));
for(auto map_iter = hash_table_map.begin(); map_iter != hash_table_map.end(); map_iter++)
{
std::cout << "first: " << map_iter->first << ", second: " << map_iter->second << std::endl;
}
// 2. key 可重复
std::_Hashtable, std::allocator, std::__detail::_Select1st,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
std::__detail::_Hashtable_traits>::value, true, false>> hash_table_multimap;
std::string ss1 = "aaa";
hash_table_multimap.insert(std::make_pair(1, ss1));
std::string ss2 = "bbb";
hash_table_multimap.insert(std::make_pair(2, ss2));
std::string ss3 = "ccc";
hash_table_multimap.insert(std::make_pair(3, ss3));
hash_table_multimap.insert(std::make_pair(3, ss1));
hash_table_multimap.insert(std::make_pair(3, ss2));
std::string ss4 = "ddd";
hash_table_multimap.insert(std::make_pair(4, ss4));
std::string ss5 = "eee";
hash_table_multimap.insert(std::make_pair(5, ss5));
std::cout << "-------------------------------" << std::endl;
for(auto map_iter = hash_table_multimap.begin(); map_iter != hash_table_multimap.end(); map_iter++)
{
std::cout << "first: " << map_iter->first << ", second: " << map_iter->second << std::endl;
}
return 0;
}
输出:
/// Base types for unordered_set.
template
using __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>;
template,
typename _Pred = std::equal_to<_Value>,
typename _Alloc = std::allocator<_Value>,
typename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>
using __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,
__detail::_Identity, _Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template,
class _Pred = std::equal_to<_Value>,
class _Alloc = std::allocator<_Value> >
class unordered_set
{
typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
......
};
class unordered_set 类模板参数解析
参数 | 说明 |
_Value | 容器键类型,对于 set而言,其 key 与value是相同的 |
_Hash | hash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash |
_Pred | 与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to |
_Alloc | 用于内存分配的分配器类型, 默认是 allocator<_Value> |
/// Base types for unordered_multiset.
template
using __umset_traits = __detail::_Hashtable_traits<_Cache, true, false>;
template,
typename _Pred = std::equal_to<_Value>,
typename _Alloc = std::allocator<_Value>,
typename _Tr = __umset_traits<__cache_default<_Value, _Hash>::value>>
using __umset_hashtable = _Hashtable<_Value, _Value, _Alloc,
__detail::_Identity,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template,
class _Pred = std::equal_to<_Value>,
class _Alloc = std::allocator<_Value> >
class unordered_multiset
{
typedef __umset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
......
};
class unordered_multiset 类模板参数解析
参数 | 说明 |
_Value | 容器键类型,对于 set而言,其 key 与value是相同的 |
_Hash | hash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash |
_Pred | 与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to |
_Alloc | 用于内存分配的分配器类型, 默认是 allocator<_Value> |
unordered_set 与 unordered_multiset 最大的区别在于 使用的 _Hashtable 类的模板参数的最后一个参数_Tr,前者使用的是 __detail::_Hashtable_traits<_Cache, true, true> ,最后一个模板参数为 true时,表示 _Hashtable 中的 key 唯一;后者使用的是 __detail::_Hashtable_traits<_Cache, true, false> ,最后一个模板参数为 false 时,表示 _Hashtable 中的 key 是可重复的
通过以上代码我们可以分析出,set 的 使用的 hashtable 底层的 key 与 value 的类型是同一种,
/// Base types for unordered_map.
template
using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;
template,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator >,
typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
using __umap_hashtable = _Hashtable<_Key, std::pair,
_Alloc, __detail::_Select1st,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template,
class _Pred = std::equal_to<_Key>,
class _Alloc = std::allocator > >
class unordered_map
{
typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
......
};
class unordered_map 类模板参数解析:
参数名 | 说明 |
_Key | 容器键类型 |
_Tp | 容器值类型 |
_Hash | hash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash |
_Pred | 与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to |
_Alloc | 用于内存分配的分配器类型, 默认是 allocator |
/// Base types for unordered_multimap.
template
using __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>;
template,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator >,
typename _Tr = __ummap_traits<__cache_default<_Key, _Hash>::value>>
using __ummap_hashtable = _Hashtable<_Key, std::pair,
_Alloc, __detail::_Select1st,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template,
class _Pred = std::equal_to<_Key>,
class _Alloc = std::allocator > >
class unordered_multimap
{
typedef __ummap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
......
};
class unordered_multimap 类模板参数解析:
参数名 | 说明 |
_Key | 容器键类型 |
_Tp | 容器值类型 |
_Hash | hash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash |
_Pred | 与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to |
_Alloc | 用于内存分配的分配器类型, 默认是 allocator |
std::unordered_set - cppreference.com
函数 | 说明 |
unordered_set | 空构造函数 |
template< class InputIt > unordered_set( InputIt first, InputIt last) |
构造拥有范围 [first, last) 的内容的容器 |
unordered_set( size_type bucket_count) | 指定 bucket 数量的构造函数 |
unordered_set(initializer_list |
通过 initializer_list构造 set的构造函数 |
函数 | 说明 |
begin | 返回 容器中第一个元素的 iterator |
end | 返回 容器中最后一个元素的下一个 iterator |
函数 | 说明 |
empty | 判断容器是否为空 |
size | 返回容器存储的元素数量 |
函数 | 说明 |
clear | 清空容器 |
insert | 向容器插入元素 |
emplace | 向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造 |
erase | 移除指定元素或者指定 位置的元素 |
函数 | 说明 |
count | 返回指定元素的数量 |
find | 返回指定元素的 iterator |
contains | 返回容器中是否存在指定元素 (c++ 20 支持) |
示例代码:
#include
#include
int main()
{
std::vector vec = {1, 2, 3, 4, 5, 6};
// vec.begin()
// 1. 初始化构造函数
std::unordered_set set1;
std::unordered_set set2(vec.begin(), vec.end(), 7);
std::unordered_set set3 = {1, 2, 3, 4, 5, 6};
std::unordered_set set4(13);
// 2. 迭代器
for(auto iter = set2.begin(); iter != set2.end(); iter++)
{
std::cout << *iter << " "; // 6 5 4 3 2 1
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
for(auto iter = set2.cbegin(); iter != set2.cend(); iter++)
{
std::cout << *iter << " "; // 6 5 4 3 2 1
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set3.size() << std::endl;
// std::cout << "contanis 1: " << set3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
set2.insert(666);
set2.insert(888);
set2.erase(4);
for(auto iter = set2.begin(); iter != set2.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
set2.clear();
std::cout << "" << std::endl;
std::cout << "empty: " << set2.empty() << std::endl;
std::cout << "size: " << set2.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << set3.count(2) << std::endl;
std::cout << "find: " << *set3.find(2) << std::endl;
set3.erase(3);
for(auto iter = set3.begin(); iter != set3.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
std::cout << "" << std::endl;
return 0;
}
输出:
函数 | 说明 |
unordered_set | 空构造函数 |
template< class InputIt > unordered_set( InputIt first, InputIt last) |
构造拥有范围 [first, last) 的内容的容器 |
unordered_set( size_type bucket_count) | 指定 bucket 数量的构造函数 |
unordered_set(initializer_list |
通过 initializer_list构造 set的构造函数 |
函数 | 说明 |
begin | 返回 容器中第一个元素的 iterator |
end | 返回 容器中最后一个元素的下一个 iterator |
函数 | 说明 |
empty | 判断容器是否为空 |
size | 返回容器存储的元素数量 |
函数 | 说明 |
clear | 清空容器 |
insert | 向容器插入元素 |
emplace | 向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造 |
erase | 移除指定元素或者指定 位置的元素 |
函数 | 说明 |
count | 返回指定元素的数量 |
find | 返回指定元素的 iterator |
contains | 返回容器中是否存在指定元素 (c++ 20 支持) |
示例代码:
#include
#include
int main()
{
std::vector vec = {1, 2, 3, 3, 3, 4, 5, 6};
// vec.begin()
// 1. 初始化构造函数
std::unordered_multiset set1;
std::unordered_multiset set2(vec.begin(), vec.end(), 7);
std::unordered_multiset set3 = {1, 2, 3, 3, 3, 4, 5, 6};
std::unordered_multiset set4(13);
// 2. 迭代器
for(auto iter = set2.begin(); iter != set2.end(); iter++)
{
std::cout << *iter << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
for(auto iter = set2.cbegin(); iter != set2.cend(); iter++)
{
std::cout << *iter << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set3.size() << std::endl;
// std::cout << "contanis 1: " << set3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
set2.insert(666);
set2.insert(888);
set2.erase(4);
for(auto iter = set2.begin(); iter != set2.end(); iter++)
{
std::cout << *iter << " ";
}
set2.clear();
std::cout << "" << std::endl;
std::cout << "empty: " << set2.empty() << std::endl;
std::cout << "size: " << set2.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << set3.count(2) << std::endl;
std::cout << "find: " << *set3.find(2) << std::endl;
set3.erase(3);
for(auto iter = set3.begin(); iter != set3.end(); iter++)
{
std::cout << *iter << " ";
}
std::cout << "" << std::endl;
return 0;
}
输出:
函数 | 说明 |
unordered_map | 空构造函数 |
template< class InputIt > unordered_map( InputIt first, InputIt last) |
构造拥有范围 [first, last) 的内容的容器 |
unordered_map( std::initializer_list |
通过 list 来构造 map 的gou'z |
函数 | 说明 |
begin | 返回 容器中第一个元素的 iterator |
end | 返回 容器中最后一个元素的下一个 iterator |
函数 | 说明 |
empty | 判断容器是否为空 |
size | 返回容器存储的元素数量 |
函数 | 说明 |
clear | 清空容器 |
insert | 向容器插入元素 |
emplace | 向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造 |
erase | 移除指定元素或者指定 位置的元素 |
函数 | 说明 |
count | 返回指定 key 的数量 |
find | 返回指定 key 的 iterator |
contains | 返回容器中是否存在指定元素 (c++ 20 支持) |
at | 返回指定 key 的引用 |
operator[] | 返回指定 key 的引用,若是不存在该 key, 则将 key 与对应的值插入容器 |
示例代码:
#include
#include
int main()
{
// 1. 初始化构造函数
std::unordered_map map1;
std::unordered_map map2 = {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}, {5, "e"}};
std::unordered_map map3(map2.begin(), map2.end());
// 2. 迭代器
for(auto iter = map2.begin(); iter != map2.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
for(auto iter = map2.cbegin(); iter != map2.cend(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map3.size() << std::endl;
// std::cout << "contanis 1: " << map3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
map2.insert({666, "fff"});
map2.emplace(888, "hhh");
map2.erase(4);
for(auto iter = map2.begin(); iter != map2.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
map2.clear();
std::cout << "" << std::endl;
std::cout << "empty: " << map2.empty() << std::endl;
std::cout << "size: " << map2.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << map3.count(2) << std::endl;
std::cout << "find: " << map3.find(2)->first << ", " << map3.find(2)->second << std::endl;
map3.erase(3);
for(auto iter = map3.begin(); iter != map3.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
std::cout << "operator[]: " << map3[1] << std::endl;
std::cout << "at: "<< map3.at(1) <
输出:
函数 | 说明 |
unordered_map | 空构造函数 |
template< class InputIt > unordered_map( InputIt first, InputIt last) |
构造拥有范围 [first, last) 的内容的容器 |
unordered_map( std::initializer_list |
通过 list 来构造 map 的gou'z |
函数 | 说明 |
begin | 返回 容器中第一个元素的 iterator |
end | 返回 容器中最后一个元素的下一个 iterator |
函数 | 说明 |
empty | 判断容器是否为空 |
size | 返回容器存储的元素数量 |
函数 | 说明 |
clear | 清空容器 |
insert | 向容器插入元素 |
emplace | 向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造 |
erase | 移除指定元素或者指定 位置的元素 |
函数 | 说明 |
count | 返回指定 key 的数量 |
find | 返回指定 key 的 iterator |
contains | 返回容器中是否存在指定元素 (c++ 20 支持) |
注意:
没有看错,unordered_multimap 不支持 at 与 operator[] 函数访问元素,因为这个容器是允许重复 key存在的,所以你打算调用 at 与 operator[] 是访问重复key 的哪个值呢?
基于此,c++ 标准针对 multimap 就不提供 at 与 operator[] 函数 了
示例代码:
#include
#include
int main()
{
// 1. 初始化构造函数
std::unordered_multimap map1;
std::unordered_multimap map2 = {{1, "a"}, {2, "b"}, {3, "c1"}, {3, "c2"},{3, "c3"},{4, "d"}, {5, "e"}};
std::unordered_multimap map3(map2.begin(), map2.end());
// 2. 迭代器
for(auto iter = map2.begin(); iter != map2.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
for(auto iter = map2.cbegin(); iter != map2.cend(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map3.size() << std::endl;
// std::cout << "contanis 1: " << map3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
map2.insert({666, "fff1"});
map2.insert({666, "fff2"});
map2.emplace(888, "hhh");
map2.erase(4);
for(auto iter = map2.begin(); iter != map2.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
map2.clear();
std::cout << "" << std::endl;
std::cout << "empty: " << map2.empty() << std::endl;
std::cout << "size: " << map2.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << map3.count(2) << std::endl;
std::cout << "find: " << map3.find(2)->first << ", " << map3.find(2)->second << std::endl;
map3.erase(3);
for(auto iter = map3.begin(); iter != map3.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " ";
}
std::cout << "" << std::endl;
return 0;
}
输出:
// my_unordered_set.h
#include
#include
#include
#include
template
class my_unordered_set
{
public:
template
using __cache_default
= std::__not_,
// Mandatory to have erase not throwing.
std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
using __traits = std::__detail::_Hashtable_traits<__cache_default<_Value,
std::hash<_Value>>::value, true, true>;
using hashtable =
std::_Hashtable<_Value, _Value,
std::allocator<_Value>,
std::_Identity<_Value>,
std::equal_to<_Value>, std::hash<_Value>,
std::__detail::_Mod_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Prime_rehash_policy,
__traits>;
// using iterator = typename hashtable::iterator;
using const_iterator = typename hashtable::const_iterator;
typedef typename hashtable::hasher my_hasher;
typedef typename hashtable::allocator_type my_allocator_type;
typedef typename hashtable::key_equal my_key_equal;
public:
my_unordered_set()
{
}
const_iterator begin() const
{
return hash_container.begin();
}
const_iterator end() const
{
return hash_container.end();
}
std::size_t size()
{
return hash_container.size();
}
bool empty()
{
return hash_container.empty();
}
void clear()
{
hash_container.clear();
}
void insert(_Value& val)
{
hash_container.insert(val);
}
void insert(_Value&& val)
{
hash_container.insert(val);
}
template
void emplace(_Args& ...args)
{
hash_container.emplace(std::forward<_Args>(args)...);
}
template
void emplace(_Args&& ...args)
{
hash_container.emplace(std::forward<_Args>(args)...);
}
void erase(_Value& val)
{
hash_container.erase(val);
}
void erase(_Value&& val)
{
hash_container.erase(val);
}
std::size_t count(_Value& val)
{
return hash_container.count(val);
}
std::size_t count(_Value&& val)
{
return hash_container.count(val);
}
const_iterator find(_Value& val)
{
return hash_container.find(val);
}
const_iterator find(_Value&& val)
{
return hash_container.find(val);
}
private:
hashtable hash_container;
};
// main.cpp
#include
#include"my_unordered_set.h"
int main()
{
// 1. 初始化构造函数
my_unordered_set set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
set1.insert(4);
set1.insert(5);
set1.insert(6);
// 2. 迭代器
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 6 5 4 3 2 1
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
// std::cout << "contanis 1: " << set3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
set1.insert(666);
set1.insert(888);
set1.erase(4);
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
std::cout << "" << std::endl;
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << set1.count(1) << std::endl;
std::cout << "find: " << *set1.find(1) << std::endl;
set1.erase(3);
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
std::cout << "" << std::endl;
set1.clear();
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
return 0;
}
输出:
// my_unordered_multiset.h
#include
#include
#include
#include
template
class my_unordered_multiset
{
public:
template
using __cache_default
= std::__not_,
// Mandatory to have erase not throwing.
std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
using _traits = std::__detail::_Hashtable_traits<__cache_default<_Value, std::hash<_Value>>::value, true, false>;
using hashtable =
std::_Hashtable<_Value, _Value, std::allocator<_Value>, std::_Identity<_Value>,
std::equal_to<_Value>, std::hash<_Value>,
std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
_traits>;
// using iterator = typename hashtable::iterator;
using const_iterator = typename hashtable::const_iterator;
public:
my_unordered_multiset()
{
}
const_iterator begin() const
{
return hash_container.begin();
}
const_iterator end() const
{
return hash_container.end();
}
std::size_t size()
{
return hash_container.size();
}
bool empty()
{
return hash_container.empty();
}
void clear()
{
hash_container.clear();
}
void insert(_Value& val){
hash_container.insert(val);
}
void insert(_Value&& val){
hash_container.insert(val);
}
template
void emplace(_Args& ...args)
{
hash_container.emplace(std::forward<_Args>(args)...);
}
template
void emplace(_Args&& ...args)
{
hash_container.emplace(std::forward<_Args>(args)...);
}
void erase(_Value& val)
{
hash_container.erase(val);
}
void erase(_Value&& val)
{
hash_container.erase(val);
}
std::size_t count(_Value& val)
{
return hash_container.count(val);
}
std::size_t count(_Value&& val)
{
return hash_container.count(val);
}
const_iterator find(_Value& val)
{
return hash_container.find(val);
}
const_iterator find(_Value&& val)
{
return hash_container.find(val);
}
private:
hashtable hash_container;
};
// main.cpp
#include
#include"my_unordered_multiset.h"
int main()
{
// 1. 初始化构造函数
my_unordered_multiset set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
set1.insert(4);
set1.insert(5);
set1.insert(6);
set1.insert(6);
set1.insert(6);
// 2. 迭代器
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 6 5 4 3 2 1
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
// std::cout << "contanis 1: " << set3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
set1.insert(666);
set1.insert(888);
set1.erase(4);
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
std::cout << "" << std::endl;
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << set1.count(2) << std::endl;
std::cout << "find: " << *set1.find(2) << std::endl;
set1.erase(3);
for(auto iter = set1.begin(); iter != set1.end(); iter++)
{
std::cout << *iter << " "; // 1 2 3 4 5 6
}
std::cout << "" << std::endl;
set1.clear();
std::cout << "empty: " << set1.empty() << std::endl;
std::cout << "size: " << set1.size() << std::endl;
return 0;
}
输出:
// my_unordered_map.h
#include
#include
#include
#include
template
class my_unordered_map
{
public:
template
using __cache_default
= std::__not_,
// Mandatory to have erase not throwing.
std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
using _traits = std::__detail::_Hashtable_traits<__cache_default>::value, false, true>;
using _HashTable = std::_Hashtable, std::allocator, std::__detail::_Select1st,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
_traits>;
// using const_iterator = typename _HashTable::const_iterator;
using iterator = typename _HashTable::iterator;
typedef typename _HashTable::value_type value_type;
my_unordered_map()
{
}
iterator begin()
{
return _hash_container.begin();
}
iterator end()
{
return _hash_container.end();
}
bool empty()
{
return _hash_container.empty();
}
std::size_t size()
{
return _hash_container.size();
}
void insert(const Key& key, Value& value)
{
iterator iter = _hash_container.find(key);
if(iter != _hash_container.end())
{
iter->second = value;
}
else
{
_hash_container.insert(std::make_pair(key, value));
}
}
void insert(const Key&& key, Value&& value)
{
insert(key, value);
}
void insert(std::initializer_list list)
{
_hash_container.insert(list);
}
void insert(value_type&& value)
{
_hash_container.insert(value);
}
template
void emplace(_Args ...args)
{
_hash_container.emplace(std::forward<_Args>(args)...);
}
void clear()
{
_hash_container.clear();
}
void erase(const Key& key)
{
_hash_container.erase(key);
}
void erase(const Key&& key)
{
_hash_container.erase(key);
}
std::size_t count(const Key& key)
{
return _hash_container.count(key);
}
std::size_t count(const Key&& key)
{
return _hash_container.count(key);
}
iterator find(const Key& key)
{
return _hash_container.find(key);
}
iterator find(const Key&& key)
{
return _hash_container.find(key);
}
Value& at(const Key& key)
{
return _hash_container.at(key);
}
Value& at(const Key&& key)
{
return _hash_container.at(key);
}
Value& operator[](const Key& key)
{
return _hash_container[key];
}
Value& operator[](const Key&& key)
{
return _hash_container[key];
}
private:
_HashTable _hash_container;
};
// main.cpp
#include
#include"my_unordered_map.h"
int main()
{
// 1. 初始化构造函数
my_unordered_map map1;
map1[1] = "a";
map1[2] = "b";
map1[3] = "c";
map1[4] = "d";
map1[5] = "e";
// 2. 迭代器
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map1.size() << std::endl;
// std::cout << "contanis 1: " << map1.contains(1) << std::endl; c++20 支持
// 4. 容器修改
map1.insert({566, "fff"});
map1.insert({{666, "fff"}});
map1.insert({std::make_pair(666, "fff")});
map1.emplace(888, "hhh");
map1.erase(4);
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map1.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << map1.count(2) << std::endl;
std::cout << "find: " << map1.find(2)->first << ", " << map1.find(2)->second << std::endl;
map1.erase(3);
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
std::cout << "operator[]: " << map1[1] << std::endl;
std::cout << "at: "<< map1.at(1) <
输出:
// my_unordered_multimap.h
#include
#include
#include
#include
template
class my_unordered_multimap
{
public:
template
using __cache_default
= std::__not_,
// Mandatory to have erase not throwing.
std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
using _traits = std::__detail::_Hashtable_traits<__cache_default>::value, false, false>;
using _HashTable = std::_Hashtable, std::allocator, std::__detail::_Select1st,
std::equal_to, std::hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
_traits>;
// using const_iterator = typename _HashTable::const_iterator;
using iterator = typename _HashTable::iterator;
typedef typename _HashTable::value_type value_type;
my_unordered_multimap()
{
}
iterator begin()
{
return _hash_container.begin();
}
iterator end()
{
return _hash_container.end();
}
bool empty()
{
return _hash_container.empty();
}
std::size_t size()
{
return _hash_container.size();
}
void insert(const Key& key, Value& value)
{
iterator iter = _hash_container.find(key);
if(iter != _hash_container.end())
{
iter->second = value;
}
else
{
_hash_container.insert(std::make_pair(key, value));
}
}
void insert(const Key&& key, Value&& value)
{
insert(key, value);
}
void insert(std::initializer_list list)
{
_hash_container.insert(list);
}
void insert(value_type&& value)
{
_hash_container.insert(value);
}
template
void emplace(_Args ...args)
{
_hash_container.emplace(std::forward<_Args>(args)...);
}
void clear()
{
_hash_container.clear();
}
void erase(const Key& key)
{
_hash_container.erase(key);
}
void erase(const Key&& key)
{
_hash_container.erase(key);
}
std::size_t count(const Key& key)
{
return _hash_container.count(key);
}
std::size_t count(const Key&& key)
{
return _hash_container.count(key);
}
iterator find(const Key& key)
{
return _hash_container.find(key);
}
iterator find(const Key&& key)
{
return _hash_container.find(key);
}
private:
_HashTable _hash_container;
};
// main.cpp
#include
#include"my_unordered_multimap.h"
int main()
{
// 1. 初始化构造函数
my_unordered_multimap map1;
map1.insert({1, "a"});
map1.insert({1, "b"});
map1.insert({2, "b"});
map1.insert({3, "c"});
// 2. 迭代器
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 3. 容器容量
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map1.size() << std::endl;
// std::cout << "contanis 1: " << map3.contains(1) << std::endl; c++20 支持
// 4. 容器修改
map1.insert({666, "fff1"});
map1.insert({666, "fff2"});
map1.emplace(888, "hhh");
map1.erase(4);
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map1.size() << std::endl;
std::cout << "" << std::endl;
std::cout << "------------------------" << std::endl;
// 5. 容器查找
std::cout << "count: " << map1.count(2) << std::endl;
std::cout << "find: " << map1.find(2)->first << ", " << map1.find(2)->second << std::endl;
map1.erase(3);
for(auto iter = map1.begin(); iter != map1.end(); iter++)
{
std::cout << iter->first << ", " << iter->second << " | ";
}
std::cout << "" << std::endl;
map1.clear();
std::cout << "empty: " << map1.empty() << std::endl;
std::cout << "size: " << map1.size() << std::endl;
std::cout << "" << std::endl;
return 0;
}
输出: