1. 基本概念
unordered_set是无序数据集,也就是没有以特别的顺序存储数据。这个是基于哈希表实现的数据容器。哈希表本质上是一个数组,与常见的数组不同的是,哈希表中存放的值是键值对。键值对就是可以根据一个键值获取对应的一个值。而对于键值,百度百科的解释是“键值(key)是windows中注册表中的概念。键值位于注册表结构链末端,和文件系统的文件类似,包含当前计算机及应用程序执行时使用的实际配置信息和数据。键值包含几种数据类型,以适应不同环境的使用需求。”通过一个键值获取对应的一个值,这个有点类似高等数学中的映射。一个简单的例子在这里说明这个概念。
假设:有一本中文词典,里面包含了所有的汉字,但是这些汉字是按任意顺序随意排版的,那么想要在其中找到某一个汉字,你就需要从头至尾一个一个核查,如果运气差,这个汉字正好在词典的末尾,那你需要遍历整本词典才能找到你要查的汉字。
优化:因为汉字和拼音之间存在着一种确定的关系,为了提高查找速度,现在将所有汉字按照拼音(key)进行排序(拼音可以根据首字母,第二个字母依次进一步排序),并且每个拼音都有一个对应页码(index),从该页开始,存放拼音对应的汉字(value)。所以找到拼音,也就能在对应的页码找到对应的汉字。其中,拼音和页码之间,有着某种固定的映射关系,可以通过某种方式计算出来(hash function)。
从上面的例子可以看出这个容器在数据查找、容器遍历方面具有相当优势,所以对于查找问题可以考虑使用该容器。另外,这个容器存储的数据是唯一的,这个特性可以用于快速检查某段数据序列是否存在重复值。
2. 用法
- 定义和初始化
// constructing unordered_sets
#include <iostream>
#include <string>
#include <unordered_set>
template<class T>
T cmerge (T a, T b) {
T t(a); t.insert(b.begin(),b.end()); return t; }
int main ()
{
std::unordered_set<std::string> first; // empty
std::unordered_set<std::string> second ( {
"red","green","blue"} ); // init list
std::unordered_set<std::string> third ( {
"orange","pink","yellow"} ); // init list
std::unordered_set<std::string> fourth ( second ); // copy
std::unordered_set<std::string> fifth ( cmerge(third,fourth) ); // move
std::unordered_set<std::string> sixth ( fifth.begin(), fifth.end() ); // range
std::cout << "sixth contains:";
for (const std::string& x: sixth) std::cout << " " << x;
std::cout << std::endl;
return 0;
输出:
sixth contains: pink yellow red green orange blue
- 成员方法
(1)begin():返回指向第一元素的迭代器(迭代器(iterable)是一个超级接口! 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦)。
原型:
<1> container iterator (1)
iterator begin() noexcept;
const_iterator begin() const noexcept;
<2> bucket iterator (2)
local_iterator begin ( size_type n );
const_local_iterator begin ( size_type n ) const;
根据函数原型,可以知道begin可以返回两种迭代器类型,其中iterator可以改变所指向元素的值,而const_iterator不可改,只可更改其指向其他元素。也就是const_iterator可以修改自身的指向,但不可以修改所指向的位置的值。
(2)end():返回指向最后一个元素的迭代器。
原型:
<1> container iterator (1)
iterator end() noexcept;
const_iterator end() const noexcept;
<2> bucket iterator (2)
local_iterator end (size_type n);
const_local_iterator end (size_type n) const;
begin和end的使用例子:
// unordered_set::begin/end example
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset =
{
"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
std::cout << "myset contains:";
for ( auto it = myset.begin(); it != myset.end(); ++it )
std::cout << " " << *it;
std::cout << std::endl;
std::cout << "myset's buckets contain:\n";
for ( unsigned i = 0; i < myset.bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for ( auto local_it = myset.begin(i); local_it!= myset.end(i); ++local_it )
std::cout << " " << *local_it;
std::cout << std::endl;
}
return 0;
}
输出:
myset contains: Venus Jupiter Neptune Mercury Earth Uranus Saturn Mars
myset's buckets contain:
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains:
bucket #4 contains: Neptune Mercury
bucket #5 contains:
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains:
bucket #10 contains:
(3) bucket(const key_type& k):返回元素值为k的桶号(在一个unordered_set内部,元素不会按任何顺序排序,而是通过元素值的hash值将元素分组放置到各个槽(Bucker,也可以译为“桶”),这样就能通过元素值快速访问各个对应的元素(均摊耗时为O(1))。类似通过字典的拼音查找某个汉字
原型:
size_type bucket ( const key_type& k ) const;
使用例子
// unordered_set::bucket
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset = {
"water","sand","ice","foam"};
for (const std::string& x: myset) {
std::cout << x << " is in bucket #" << myset.bucket(x) << std::endl;
}
return 0;
}
输出:
ice is in bucket #0
foam is in bucket #2
sand is in bucket #2
water is in bucket #4
(4) bucket_count(): 返回容器中桶的数量
方法原型:
size_type bucket_count() const noexcept;
使用例子
// unordered_set::bucket_count
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset =
{
"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
unsigned n = myset.bucket_count();
std::cout << "myset has " << n << " buckets.\n";
for (unsigned i=0; i<n; ++i) {
std::cout << "bucket #" << i << " contains:";
for (auto it = myset.begin(i); it!=myset.end(i); ++it)
std::cout << " " << *it;
std::cout << "\n";
}
return 0;
}
输出
myset has 11 buckets.
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains:
bucket #4 contains: Neptune Mercury
bucket #5 contains:
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains:
bucket #10 contains:
(5)cbegin()和cend(),这两者的功能和begin()、cend()相同,只不过返回类型不一样,cbegin和cend返回的都是const_iterator。
方法原型:
container iterator (1)
const_iterator cbegin() const noexcept;
bucket iterator (2)
const_local_iterator cbegin ( size_type n ) const;
container iterator (1)
const_iterator cend() const noexcept;
bucket iterator (2)
const_local_iterator cend ( size_type n ) const;
这里有两种返回类型,一种是常见的迭代器类型(const_iterator),而另一种是const_local_iterator类型。顾名思义,局部的迭代器,也就是回去当前这个桶的迭代器。使用的例子如下:
// unordered_set::cbegin/cend example
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset =
{
"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
std::cout << "myset contains:";
for ( auto it = myset.cbegin(); it != myset.cend(); ++it )//这里的it是const_iterator类型
std::cout << " " << *it; // cannot modify *it
std::cout << std::endl;
std::cout << "myset's buckets contain:\n";
for ( unsigned i = 0; i < myset.bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for ( auto local_it = myset.cbegin(i); local_it!= myset.cend(i); ++local_it )//这里的local_it就是const_local_iterator类型
std::cout << " " << *local_it;
std::cout << std::endl;
}
return 0;
}
输出
myset contains: Venus Jupiter Neptune Mercury Earth Uranus Saturn Mars
myset's buckets contain:
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains:
bucket #4 contains: Neptune Mercury
bucket #5 contains:
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains:
bucket #10 contains:
(6) clear() :清除容器中数据。这个方法会调用容器的析构方法~unorder_set。需要注意的是使用clear( )并不会清空内存,只是把容器中存储的数据清除掉,也就是在使用clear()后,容器内的元素个数为0,所申请的内存并没有释放掉。那如何释放该内存呢?BOOM朝朝朝博主总结了三种容器释放内存的方法,这里直接贴出来:
1.方法一:直接声明同一个匿名容器类型与原有容器交换,匿名容器会自动销毁;
vector( ).swap(num);
2.方法二:先声明一个临时对象,然后与目标容器交换数据:
vector temp;
(temp).swap(num);
临时对象未被初始化,其缓冲区大小为0,没有数据,与目标对象交换数据,则容器num中的缓冲区就没有了;
3.方法三:先将目标容器的内存清空,再利用swap函数与原有容器进行交换,即:
num.clear( ); vector(num).swap(num);
方法原型:
void clear() noexcept;
noexcept,它有两类作用:noexcept 指定符和noexcept 运算符。其中指定符是指定函数是否抛出异常,而运算符是进行编译时检查,若表达式声明为不抛出任何异常则返回true。
使用例子
// clearing unordered_set
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset =
{
"chair", "table", "lamp", "sofa" };
std::cout << "myset contains:";
for (const std::string& x: myset) std::cout << " " << x;
std::cout << std::endl;
myset.clear();
myset.insert("bed");
myset.insert("wardrobe");
myset.insert("nightstand");
std::cout << "myset contains:";
for (const std::string& x: myset) std::cout << " " << x;
std::cout << std::endl;
return 0;
}
输出
myset contains: sofa lamp table chair
myset contains: nightstand wardrobe bed
(7)count(const key_type& k): 统计容器中元素值为k的数量。由于unordered_set存储的元素是唯一的,所以这个方法只会返回0或1。
方法原型:
size_type count ( const key_type& k ) const;
使用例子
// unordered_set::count
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset = {
"hat", "umbrella", "suit" };
for (auto& x: {
"hat","sunglasses","suit","t-shirt"}) {
if (myset.count(x)>0)
std::cout << "myset has " << x << std::endl;
else
std::cout << "myset has no " << x << std::endl;
}
return 0;
}
输出
myset has hat
myset has no sunglasses
myset has suit
myset has no t-shirt
(8)emplance(Args&&… args):当容器中没有args元素时,向容器中插入数据args并且返回元素的迭代器和一个True变量。如果容器中已有该元素将返回该元素的迭代器和一个False变量。
方法原型:
template <class... Args>
pair <iterator,bool> emplace ( Args&&... args );
使用例子
// unordered_set::emplace
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset;
myset.emplace ("potatoes");
myset.emplace ("milk");
myset.emplace ("flour");
std::cout << "myset contains:";
for (const std::string& x: myset) std::cout << " " << x;
std::cout << std::endl;
return 0;
}
输出
myset contains: potatoes flour milk