介绍
概念上来说, unordered 容器以一种随意顺序包含你安插进去的所有元素,相比于 set/multiset 和 map/multimap 而言,不需要排序准则;相比于 sequence 容器,没有语义可以用来放置元素到指定位置
像 associate 容器一样,个别 class 会有不同:
- Unordered set 和 multiset 存放的都是某特定类型的个别 value,而 Unordered map 和 multimap 存放的元素是 key/value 的 pair 类型,其中一般 key 为查找或存放依据
- Unordered set 和 map 不允许存放相同数据,但 Unordered map 和 multimap 允许
头文件:
- #include <unordered_set>
- #include <unordered_map>
定义:
namespace std{
template <typename T,
typename Hash = hash<T>,
typename EqPred = equal_to<T>,
typename Allocator = allocator<T>>
class unordered_set;
template <typename T,
typename Hash = hash<T>,
typename EqPred = equal_to<T>,
typename Allocator = allocator<T>>
class unordered_multiset;
template <typename Key, typename Value
typename Hash = hash<T>,
typename EqPred = equal_to<T>,
typename Allocator = allocator<T>>
class unordered_map;
template <typename Key, typename Value
typename Hash = hash<T>,
typename EqPred = equal_to<T>,
typename Allocator = allocator<T>>
class unordered_multimap;
}
template Hash 类型是哈希函数,如果不自定义则会默认使用标准库的 hash
template Eqpred 是定义等价准则:这是一个判断式,用来查找元素来判断两个 value 是否相等,如果没有自定义则默认使用 equal_to<> ,它会以 operator == 比较两个元素
Unordered 容器能力
所有标准化 unordered container class 但是以 hash table 为基础,并基于以下假设:
- hash table 采用 chaining(链表)做法,于是一个 hash code 将会被关联到一个 linked list
- 该 linked list 的链类型取决于实现,C++ standard 仅仅保证它们的 iterator 至少是 forward iterator
- 关于 rehashing,有不同的实现策略:(1)在单一的 insert 或 erase 动作出现时,有时会发生一次 rehash;(2)渐进地改变 hash table的大小
内部结构
这里展示了 Unordered 容器的典型内部结构,我们可以发现对于每个将被存放的元素,hash function 会把 key 映射至 hash table 内的某个 bucket 中,然后每个 bucket 都管理一个 单向的 linked list,内含经过 hash function 产生相同结果的元素
在不存在哈希冲突的情况下,可以以常数级别的时间复杂度进行删除、查询等操作
创造和控制 Unordered 容器
创造、复制和销毁
Unord c | Default 默认构造函数 |
Unord c(bnum) | 建立一个内部至少有 bnum 个 bucket 的容器 |
Unord c(bnum,hf) | 建立一个内部至少有 bnum 个 bucket 的容器,并且以 hf 为 hash function |
Unord c(bnum, hf cmp) | 建立一个内部至少有 bnum 个 bucket 的容器,并且以 hf 为 hash function,并以 cmp 为等价准则 |
Unord c(c2) | Copy 构造函数 |
Unord c = (c2) | Copy 构造函数 |
Unord c(rv) | Move 构造函数 |
Unord c = rv | Move 构造函数 |
Unord c(beg,end) | 以区间[beg,end)内的数值为初值构造 |
Unord c(beg,end,bnum) | 以区间[beg,end)内的数值为初值构造,并且内部至少有 bnum 个 bucket |
Unord c(beg,end,bnum,hf) | 以区间[beg,end)内的数值为初值构造,并且内部至少有 bnum 个 bucket,并且以 hf 为 hash function |
Unord c(beg,end,bnum,hf,cmp) | 以区间[beg,end)内的数值为初值构造,并且内部至少有 bnum 个 bucket,并且以 hf 为 hash function,并以 cmp 为等价准则 |
Unord c(initlist) | 以初值列 initlist 为初值构造 |
Unord c = initlist | 以初值列 initlist 为初值构造 |
c.~Unord() | 析构函数 |
c.max_load_factor(float) | 设置最大负荷系数 |
布局操作
c.hash_function() | 返回 hash() 函数 |
c.key_eq() | 返回 “相等性判断式” |
c.bucket_count() | 返回当前 bucket 数量 |
c.max_bucket_count() | 返回最大可能 bucket 数量 |
c.load_factor() | 返回当前负载系数 |
c.max_load_factor() | 返回当前最大负载系数 |
c.max_load_factor(val) | 设置最大负载系数 |
c.rehash(bnum) | 将容器 rehash ,使其 bucket 个数至少为 bnum |
c.reserve(num) | 将容器 rehash,使其空间至少拥有 num 个元素 |
rehash 和 reserve的区别:
- rehash 传入的 bnum值 是允许最大容量 除于最大负载系数的值
- reserve 传入的 num值 可以直接为 最大容量(即rehash的界限)
非更易型操作(Nonmodifying Operation)
c.empty() | 返回容器是否为空 |
c.size() | 返回目前元素个数 |
c,max_size() | 返回元素个数的最大可能 |
c1 == c2 | 判断 c1 是否等于 c2 |
c1 != c2 | 判断 c1 是否不等于 c2 |
特殊的查找操作(Special Search Operator)
c.count(val) | 返回 元素值为 val 的元素个数 |
c.find(val) | 返回 元素值为 val 的第一个元素,找不到则返回 val |
c.equal_range(val) | 返回 val 可被安插的第一个位置和最后一个位置,也就是 元素值 == val 的区间 |
Assignment
c = c2 | 将 c2 全部元素赋值给 c1 |
c = rv | move assignment |
c = initlist | 将初值列 的所有元素赋值给 c |
c1.swap(c2) | 置换数据 |
swap(c1,c2) | 置换数据 |
迭代器相关函数(Iterator Function)
c.begin() | 返回指向第一个元素的 bidirectional iterator |
c.end() | 返回指向最末元素的 bidirectional iterator |
c.cbegin() | 返回指向第一个元素的 const bidirectional iterator |
c.cend() | 返回指向最末元素的 const bidirectional iterator |
c.rbegin() | 返回反向指向第一个元素的 bidirectional iterator |
c.rend() | 返回反向指向最末元素的 bidirectional iterator |
c.crbegin() | 返回反向指向第一个元素的 const bidirectional iterator |
c.crend() | 返回反向指向最末元素的 const bidirectional iterator |
Unordered 容器不允许直接元素访问,所以必须使用 range_based for循环或者iterator,由于 iterator 只保证是 forward iterator,不支持 bidirectional iterator 或 random-access iterator,因此在算法的选择上应该注意区分
对于 unordered set/multiset 而言, 它的 value 值是 const;对于 unordered map/multimap 而言,它的 key 是 const。这取决于 const 的值与 元素所在位置有关,除 hash function 之外不允许修改
安插和移除函数(Inserting and Removing Element)
c.insert(val) | 安插一个 val 的拷贝,返回新元素位置 |
c.insert(pos,val) | 安插一个 val 的拷贝,返回新元素位置(pos是暗示起点) |
c.insert(beg,end) | 将[beg,end)范围内的元素全部拷贝插入 |
c.insert(initlist) | 将初值列 中的元素全部拷贝插入 |
c.emplace(args...) | 安插一个以 args 为初值的元素,并返回新元素位置 |
c.empalce_hint(pos,args...) | 安插一个以 args 为初值的元素,并返回新元素位置,pos 是起点暗示 |
c.erase(val) | 移除所有 元素值等于 val 的元素,并返回移除个数 |
c.erase(pos) | 移除 iterator 为 pos 上的元素,无返回值 |
c.earse(beg,end) | 移除 iterator [beg,end)区间范围内的元素,无返回值 |
c.clear() | 移除所有元素 |
一般而言,erasing 函数并不会令指向元素的 iterator 和reference 失效,然而 insert 和 emplace 则可能使所有的 iterator 失效,但不会影响 reference 的有效性。所以 rehash 之所以发生是因为一次安插动作造成最终的元素数量个数大于等于 bucket 数量乘以最大负载系数
insert 和 earse 的差别:
Unordered set:
pair<iterator, bool> insert(const value_type& val);
iterator insert(iterator posHint, const value_type& val);
template<typename... Args>
pair<iterator, bool> emplace(Args&&... args);
template<typename... Args>
interator emplace_hint(const_interator posHint, Args&&... args);
Unordered multiset:
iterator insert(const value_type& val);
iterator insert(iterator posHint, const value_type& val);
template<typename... Args>
iterator emplace(Args&&... args);
template<typename... Args>
interator emplace_hint(const_interator posHint, Args&&... args);
由于 set(map一样)的元素的不可重复性,所以在插入时的返回是一个 pair 类型,first 时 元素 iterator,second 是是否成功插入
Bucket 接口
c.bucket_count() | 返回当前 bucket 个数 |
c.bucket(val) | 返回 val 对应的 bucket 编号 |
c.bucket_size(buckidx) | 返回 第 buckidx 个 bucket 所含元素个数 |
c.begin(buckidx) | 返回一个 forward iterator,指向第 buckidx 个 bucket的第一元素 |
c.end(buckidx) | 返回一个 forward iterator,指向第 buckidx 个 bucket的最末元素 |
c.cbegin(buckidx) | 返回一个 const forward iterator,指向第 buckidx 个 bucket的第一元素 |
c.cend(buckidx) | 返回一个 const forward iterator,指向第 buckidx 个 bucket的最末元素 |
使用 Unordered Map 作为 Associative Array
c.[key] | 安插一个带 key 的元素——如果不存在的话,返回 reference 指向带着 key 的元素 |
c.at[key] | 返回一个 reference 指向带着 key 的元素 |