unordered_map&&unordered_set

本文深入解析C++ STL中的unordered_map和unordered_set底层实现原理,包括哈希函数、数据桶管理、冲突解决策略及迭代器设计。探讨了构造、析构、赋值、容量、修改、查询等操作,并对比了unordered_map与unordered_set、unordered_multimap之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

unordered_map&&unordered_set

底层实现

​ 在STL中,C++11引入了unordered_map、unordered_set、unordered_multimap、unordered_multimap。尽管不是名字不是哈希表,但是底层仍然是。相比于map等,这个查找的平均时间更加快。看看它的底层实现(l来自《STL源码剖析》):

//以下是基于gnu2.9版本的提取的
template<class Value, class Key, class HashFcn, 
		 class ExtractKey, class EqualKey, 
		 class Alloc=alloc>
class hashtable{
    public:
    	typedef HashFcn hasher;
    	typedef EqualKey Key_equal;
    	typedef size_t size_type;
   private:
    	hasher hash;//哈希函数(可以是函数指针或函数对象(仿函数),返回值需要是bool值)
    	key_equal equals;//key的判断相等函数(可以是函数指针或函数对象(仿函数)返回值需要是bool值)
    	ExtractKey get_key;//key的提取函数,从value_type中提取出key
    	
    	typedef _hashtable_node<Value> node;
    
    	vector<node*,Alloc> buckets;//存储桶,可见后面的图示
    	size_type num_elements;//存储桶的数量
    public:
    	size_type bucket_count() const { return buckets.size()}
    ……
};
template<class Value>
struct _hashtable_node{
    _hashtable_node* next;
    Value val;
};
//hashtable的迭代器设计
template<class Value, class Key, class HashFcn, 
		 class ExtractKey, class EqualKey, 
		 class Alloc>
struct _hashtable_iterator{
    ……
    node* cur;
    hashtable* ht;
};      


//GNU 7.2.0 中重新分解了各种容器的实现细节。此处贴出unordered_map的模板参数部分,也就是我们经常使用的。
template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;
//7.2.0版本已经有默认的hash函数(根据key类型选择的hash函数),只需要定义key与value类型即可。

内存结构:类似deque的分段存储。采用链地址法解决冲突。

在这里插入图片描述

构造、析构、赋值

构造

h函数原型(C++14)
empty(1)unordered_map();
//这里的n是什么意思?指定的是数据桶的数量
explicit unordered_map ( size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
explicit unordered_map ( const allocator_type& alloc );
unordered_map ( size_type n, const allocator_type& alloc );
unordered_map ( size_type n, const hasher& hf, const allocator_type& alloc );
range (2)template< class InputIterator>
unordered_map ( InputIterator first, InputIterator last,
size_type n = /* see below */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
template< class InputIterator>
unordered_map ( InputIterator first, InputIterator last,
size_type n, const allocator_type& alloc );
template< class InputIterator>
unordered_map ( InputIterator first, InputIterator last,
size_type n, const hasher& hf, const allocator_type& alloc );
copy (3)unordered_map ( const unordered_map& ump );
unordered_map ( const unordered_map& ump, const allocator_type& alloc );
move (4)unordered_map ( unordered_map&& ump );
unordered_map ( unordered_map&& ump, const allocator_type& alloc );
initializerlist(5)unordered_map ( initializer_list il,
size_type n = /* see below */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
unordered_map ( initializer_list il,
size_type n, const allocator_type& alloc );
unordered_map ( initializer_list il,
size_type n, const hasher& hf, const allocator_type& alloc );

上述的size_type n;均是指定指数据桶的数量。初始化一个哈希表的时候注意以下数据桶的数量,数据多,而数据桶少,就会使得查找效率更低。

析构

~unordered_map();

赋值

函数类型函数原型
copy (1)unordered_map& operator= ( const unordered_map& ump );
move (2)unordered_map& operator= ( unordered_map&& ump );
initializer list (3)unordered_map& operator= ( intitializer_list il );

迭代器与容器大小

Iterators函数说明及其函数原型
begin
cbegin
returns an iterator to the beginning (public member function)
iterator begin(); (since C++11)
const_iterator begin() const; (since C++11)
const_iterator cbegin() const; (since C++11)
end
cend
returns an iterator to the end (public member function)
iterator end(); (since C++11)
const_iterator end() const; (since C++11)
const_iterator cend() const; (since C++11)

​ 通过迭代器访问的是值类型。注意unordered_map的值类型是pair<Key,Value>。而unordered_set的值类型就是Key。另外,unordered_map是forward_iterator_tag。单链表类似的访问方式。

Capacity函数说明及其函数原型
emptychecks whether the container is empty (public member function)
bool empty() const; (since C++11)
sizereturns the number of elements (public member function)
size_type size() const; (since C++11)
max_sizereturns the maximum possible number of elements (public member function)
size_type max_size() const; (since C++11)

修改

各个操作无论是传入参数,还是返回值与map类似。

Modifiers函数说明及其函数原型
clearclears the contents (public member function)
void clear(); (since C++11)
insertinserts elements or nodes (since C++17) (public member function)
1-2返回的是,插入成功,返回插入元素的迭代器,以及true。若已有key,插入失败,返回的是已有元素的迭代器,以及false。
(1) pair<iterator,bool> insert ( const value_type& val );
(2) template < class P>
pair<iterator,bool> insert ( P&& val );
3-4返回的是,插入成功,返回插入元素的迭代器。若已有key,插入失败,返回的是已有元素的迭代器。
(3) iterator insert ( const_iterator hint, const value_type& val );
(4) template < class P>
iterator insert ( const_iterator hint, P&& val );
(5) template < class InputIterator>
void insert ( InputIterator first, InputIterator last );
(6) void insert ( initializer_list<value_type> il
insert_or_assign(C++17)inserts an element or assigns to the current element if the key already exists (public member function),插入后会改变value值(即使有相同key元素)
emplaceconstructs element in-place (public member function)即便有相同key的元素,也会构造新元素。
template <class… Args>
pair<iterator, bool> emplace ( Args&&… args );
emplace_hintconstructs elements in-place using a hint (public member function),在指引迭代器附近插入。可以提高插入效率。
template <class… Args>
iterator emplace_hint ( const_iterator position, Args&&… args );
try_emplace (C++17)inserts in-place if the key does not exist, does nothing if the key exists (public member function)如果有相同key元素,不做任何事。
template <class… Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&… args);
(1) (since C++17)
template <class… Args>
pair<iterator, bool> try_emplace(key_type&& k, Args&&… args);
(2) (since C++17)
template <class… Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&… args);
(3) (since C++17)
template <class… Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&… args);
(4) (since C++17)
eraseerases elements (public member function)三种方式删除元素
by position (1)
iterator erase ( const_iterator position );
by key (2)
size_type erase ( const key_type& k );
range (3)
iterator erase ( const_iterator first, const_iterator last );
swapswaps the contents (public member function),交换指针一些东西,效率挺高
void swap ( unordered_map& ump );
extract (C++17)extracts nodes from the container (public member function)
node_type extract( const_iterator position ); (1) (since C++17)
node_type extract( const key_type& x ); (2) (since C++17)
merge (C++17)splices nodes from another container (public member function)
template<class H2, class P2>
void merge(std::unordered_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(std::unordered_map<Key, T, H2, P2, Allocator>&& source);

notes:

对于unorder_map,每个数据桶只能装一个元素。

对于extract:(唯一一种改变元素key值而不需要重新申请内存分配的,要使用move)

extract is the only way to change a key of a map element without reallocation。

merge:从source中提取不同key的元素(是对于* this的hash函数以及key相等函数的意义上的key不同)。* this与source要求分配器类型是一致的。

另外,无论是insert,emplace等,他们如果增加了元素。会在当前size()>最大装载因子*数据桶数量的时候进行rehash。rehash的意思是:增大数据桶的数量。装载因子等于

查询

在unordered_multimap中无at,operator[ ]。

Lookup
ataccess specified element with bounds checking (public member function)注意返回的是mapped_type。
mapped_type& at ( const key_type& k );
const mapped_type& at ( const key_type& k ) const;
operator[ ]access specified element (public member function)
mapped_type& operator[] ( const key_type& k );
mapped_type& operator[] ( key_type&& k );
countreturns the number of elements matching specific key (public member function),返回特定key的元素数量
size_type count ( const key_type& k ) const;
findfinds element with specific key (public member function),两种寻找方式,通过迭代器或通过key寻找元素
iterator find( const Key& key ); (1)
const_iterator find( const Key& key ) const; (2)
equal_rangereturns range of elements matching a specific key (public member function)左闭右开区间,包含所有特定的key。
std::pair<iterator,iterator> equal_range( const Key& key ); (since C++11)
std::pair<const_iterator,const_iterator> equal_range( const Key& key ) const;
(since C++11)

数据桶信息

数据通接口函数原型及其说明
begin(int)
cbegin(int)
returns an iterator to the beginning of the specified bucket (public member function)返回指定数据桶的第一个元素的迭代器
local_iterator begin( size_type n );
(since C++11)
const_local_iterator begin( size_type n ) const;
(since C++11)
const_local_iterator cbegin( size_type n ) const;
end(int)
cend(int)
returns an iterator to the end of the specified bucket (public member function)
local_iterator end( size_type n );
(since C++11)
const_local_iterator end( size_type n ) const;
(since C++11)
const_local_iterator cend( size_type n ) const;
(since C++11)
bucket_countreturns the number of buckets,返回桶的数量 (public member function)
size_type bucket_count() const;
(since C++11)
max_bucket_countreturns the maximum number of buckets ,返回桶的最大数量(public member function)
size_type max_bucket_count() const;
(since C++11)
bucket_sizereturns the number of elements in specific bucket,返回特定桶中的元素数量 (public member function)
size_type bucket_size( size_type n ) const;
(since C++11)
bucketreturns the bucket for specific key, 返回key对应的桶编号(public member function)
size_type bucket( const Key& key ) const;
(since C++11)

哈希策略

Hash policy
load_factorreturns average number of elements per bucket (public member function),装载因子就是元素个数除以数据桶个数
float load_factor() const;
(since C++11)
max_load_factormanages maximum average number of elements per bucket (public member function),如果超过这个装载因子就会重新设置桶的数量。默认最大装载因子为1,可以自己设置。
float max_load_factor() const;
(1) (since C++11)
void max_load_factor( float ml );
(2) (since C++11)
rehashreserves at least the specified number of buckets. This regenerates the hash table. (public member function),如果设置的桶数量,超过最大装载因子不会重新hash。
void rehash( size_type count );
(since C++11)
reservereserves space for at least the specified number of elements. This regenerates the hash table.由元素数量申请空间(就是hash表的空间) (public member function)
void reserve( size_type count );
(since C++11)

一些仿函数

Observers
hash_functionreturns function used to hash the keys (public member function)
hasher hash_function() const;
(since C++11)
key_eqreturns the function used to compare keys for equality (public member function)
key_equal key_eq() const;
(since C++11)

与unordered_set等的关系

​ 就像map与set一样,unordered_map与unordered_set之间最要注意的区别就是值类型不一致,一个是pair<key,map_type>,另外一个key类型就是值类型。而unordered_map与unordered_multimap,unordered_map要求相同的key的元素只有一个,而unordered_multimap,就可以有多个相同的key元素。

Reference

[1] https://www.bilibili.com/video/av45108908

[2]http://www.cplusplus.com/reference/unordered_map/

[3]候捷,《STL源码剖析》

### 回答1: CentOS 7启动httpd服务失败可能有多种原因,以下是一些常见的解决方法: 1. 检查httpd配置文件是否正确:可以使用命令`httpd -t`检查httpd配置文件是否正确,如果有错误,需要修改配置文件。 2. 检查端口是否被占用:可以使用命令`netstat -tlnp`查看端口是否被占用,如果被占用需要释放端口或修改httpd配置文件中的端口号。 3. 检查httpd服务是否安装:可以使用命令`rpm -qa | grep httpd`查看httpd服务是否安装,如果没有安装需要先安装httpd服务。 4. 检查httpd服务是否启动:可以使用命令`systemctl status httpd`查看httpd服务是否启动,如果没有启动需要使用命令`systemctl start httpd`启动httpd服务。 5. 检查SELinux是否开启:如果SELinux开启,可能会导致httpd服务启动失败,需要使用命令`setenforce 0`关闭SELinux,或者修改SELinux策略。 以上是一些常见的解决方法,如果以上方法都无法解决问题,可以查看httpd服务日志文件,找到具体的错误信息,然后根据错误信息进行解决。 ### 回答2: CentOS 7上的httpd服务启动失败可能有多种原因。以下列出了一些常见问题和解决方法: 1. 端口被占用 当httpd试图占用已被其他程序占用的端口时会启动失败。此时可以通过使用`netstat -tunlp`命令检查端口占用情况,然后杀死占用该端口的进程及时释放端口。或者修改httpd的配置文件,将端口修改为未被占用的端口。 2. 配置文件错误 有时httpd服务的配置文件中可能出现错误,例如语法错误或路径错误等等。在启动httpd服务之前,可以使用`apachectl configtest`命令进行检查,如果输出“Syntax OK”,则表示配置文件没有错误。如果出现错误,则需要根据错误提示进行相应修改。 3. 依赖关系问题 如果httpd依赖的其他程序或库缺失,也会导致启动失败。可以通过使用`systemctl status httpd.service`命令来查看httpd服务状态,如果输出“Failed to start”或“Loaded: failed”,则需要检查依赖关系是否完整。 4. SELinux问题 当SELinux启用时,有时会导致httpd服务启动失败。在这种情况下,可以在SELinux上禁用httpd服务,或者修改httpd配置文件解决SELinux相关的问题。 5. 用户权限问题 httpd服务启动可能需要特定的用户权限。如果使用的用户权限不够,则无法启动。可以尝试使用root用户启动httpd服务,或者根据需要修改相应的用户权限。 ### 回答3: CentOS 7中的Apache HTTP服务器(httpd)是一个常见的Web服务器,如果遇到httpd服务启动失败的情况,可能会影响服务器正常的工作和对外服务的稳定性。本文将提供一些可能会导致httpd服务启动失败的原因,并给出相应的解决方法。 1. 端口被占用 如果端口被其他进程占用,httpd服务就无法启动。可以通过 netstat -tulpn 命令查看端口占用情况,并杀死占用该端口的进程。如果端口被 httpd 服务自身占用,可以通过 systemctl restart httpd 命令重启 httpd 服务;如果是其他进程占用了端口,可以通过 kill 命令杀死该进程或更改 httpd.conf 文件配置,将 httpd 服务的端口改为其他空闲端口,重新启动。 2. 配置文件错误 httpd 服务的配置文件通常是 /etc/httpd/conf/httpd.conf,如果其中存在语法错误、权限问题或者其它配置错误,可能会导致 httpd 服务启动出错。可以通过将 httpd.conf 文件备份后删掉,重新执行 yum install httpd 命令安装 httpd 服务,然后手动修改 httpd.conf 文件,逐个检查每个配置项是否正确,确认无误后重启 httpd 服务。 3. SELinux 问题 SELinux 是 CentOS 7中提供的一种安全模块,它可以对系统文件和应用程序进行安全管控。如果 SELinux 配置不正确,可能会阻止 httpd 服务正常启动。可以通过修改 /etc/selinux/config 文件中 SELINUX=disabled 来暂时关闭 SELinux,然后重新启动 httpd 服务;或者一个更优的方式是,根据日志确定问题原因,使用命令 semanage 或者 setsebool 等工具将相关目录或者配置加入到 SELinux 许可列表中,重新启动 httpd 服务,以恢复服务正常工作。 4. 防火墙问题 如果你的 CentOs 7 服务器启用了防火墙,有可能会导致 httpd 服务启动失败。可以通过检查防火墙相关配置来确定问题原因,解决方案是修改防火墙规则,将端口 80 或者 443 等 httpd 服务需要的端口放行,重新启动 httpd 服务。 总之,当遇到 httpd 服务启动失败时,不要慌张,可以先通过日志或者执行命令查看错误信息,找到错误原因,然后根据错误原因一步一步解决问题。在解决问题过程中注意备份原始配置文件,以免造成不必要的损失。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值