unordered_set和unordered_map的使用【STL】

简介: unordered_set和unordered_map的使用【STL】

1. unordered系列关联式容器

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查找时效率可达到O ( l o g 2 N ) O(log_2 N)O(log2N),即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查找效率也不理想。效率最高的查找方式是:进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同。

实际上,unordered_map和unordered_set从名字看来,它储存的元素是unordered(无序的),而在使用上大体和set和map并无二致。

不同的是,unordered系列的容器使用了哈希结构,这也是它们存储的元素不在有序的原因。由于STL良好的封装,使得即使底层实现不同,但上层使用上还是类似的。

友情链接:map和set

unordered的命名的历史背景:

如果在现在看来,包括Java标准库也是这样设计的:根据底层实现方式的不同,set和map都以设计结构为前缀,例如:TreeMap、TreeSet;HashMap、HashSet等。

C++标准库在早期并没有将哈希结构的map和set另外作为一类容器,而是将以红黑树为底层结构的map和set作为内置容器,原因是当时计算机的CPU的算力和实际数据量还无需使用查找效率更高的哈希结构封装的map和set。但是已经推出的标准无法改变,C++的某个语言标准中提出了增加hash_*类模板,最终接受为unordered_*

2. unordered_set

2.1 unordered_set的介绍

  • unordered_set是存储<key, value>键值对的关联式容器,其允许通过key值快速的索引到与其对应的value值;键值key通常用于唯一地标识元素,而value值是一个对象,它的内容和键值key关联;
  • unordered_set没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_set将相同哈希值的键值对放在相同的桶中;
  • unordered_set容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低;
  • 它只有单向迭代器。

2.2 unordered_set的使用

unordered_set的模板参数列表

头文件

#include <unordered_set>

模板参数列表

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
  • Key:容器中存储元素的类型;
  • Hash:确定元素存储位置所用的哈希函数;
  • KeyEqual:判断各个元素是否相等所用的函数;
  • Allocator:指定分配器对象的类型(暂不做了解)。
参数 含义
Key 确定容器存储元素的类型,如果将unordered_set看做是存储键和值相同的键值对的容器,则此参数则用于推导各个键值对的键和值的类型,因为它们是完全相同的,因此一定是同一数据类型的数据。
Hash=hash<Key> 指定unordered_set容器底层存储各个元素时,所使用的哈希函数。需要注意的是,默认哈希函数hash<Key>只适用于基本数据类型(包括 string 类型),而不适用于自定义类型。
Pred=equal_to<Key> unordered_set容器内部不能存储相同的元素,而衡量2个元素是否相等的标准,取决于该参数指定的函数。默认情况下,使用STL标准库中提供的equal_to<key>规则,该规则仅支持可直接使用==运算符做比较的数据类型。

unordered_set的构造器

函数声明 功能
unordered_set(size_type()) {} 构造空的unordered_set容器
unordered_set ( InputIterator first, InputIterator last); 用[first, last)区间中的元素构造 unordered_set
unordered_set( unordered_set&& other ); unordered_set的拷贝构造

示例:

void unordered_set_test1()
{
  unordered_set<int> us1;              // 构造int类型的空容器
  string str = "hello world";
  unordered_set<char> us2(str.begin(), str.end()); // 使用迭代器区间构造
  unordered_set<int> us3(us1);           // 拷贝构造
}

unordered_set常用接口

成员方法 功能
empty() 若容器为空,则返回true;否则false。
size() 返回当前容器中存有元素的个数。
max_size() 返回容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
find(key) 查找以值为key的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果end()方法返回的迭代器)。
count(key) 在容器中查找值为key的元素的个数。
equal_range(key) 返回一个pair对象,其包含2个迭代器,用于表明当前容器中值为key的元素所在的范围。
emplace() 向容器中添加新元素,效率比insert()方法高。
emplace_hint() 向容器中添加新元素,效率比insert()方法高。
insert() 向容器中添加新元素。
erase() 删除指定元素。
clear() 清空容器,即删除容器中存储的所有元素。
swap() 交换2个unordered_set容器存储的元素,前提是必须保证这2个容器的类型完全相等。
bucket_count() 返回当前容器底层存储元素时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count() 返回当前系统中,unordered_set 容器底层最多可以使用多少桶。
bucket_size(n) 返回第n个桶中存储元素的数量。
bucket(key) 返回值为key的元素所在桶的编号。
load_factor() 返回unordered_set容器中当前的负载因子。负载因子,指的是的当前容器中存储元素的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者设置当前unordered_set容器的负载因子。
rehash(n) 将当前容器底层使用桶的数量设置为n。
reserve() 将存储桶的数量(也就是bucket_count()方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function() 返回当前容器使用的哈希函数对象。

迭代器相关

成员方法 功能
begin() 返回指向容器中第一个元素的正向迭代器。
end(); 返回指向容器中最后一个元素之后位置的正向迭代器。
cbegin() 和begin()功能相同,只不过其返回的是const类型的正向迭代器。
cend() 和end()功能相同,只不过其返回的是const类型的正向迭代器。

unordered_set没有反向迭代器。

示例

void unordered_set_test2()
{
  unordered_set<int> us;
  us.insert(1);
  us.insert(1); // 去重
  us.insert(2);
  us.insert(5);
  us.insert(4);
  us.insert(3);
  us.insert(6);
  for (auto e : us) // 使用范围for遍历
  {
    cout << e << " ";
  }
  cout << endl;
  unordered_set<int>::iterator pos = us.find(2); // 找到key为2的位置
  us.erase(pos);                   // 删除key为2的元素
  unordered_set<int>::iterator it = us.begin();
  while (it != us.end())              // 迭代器遍历 
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;
  cout << us.count(1) << endl; // 容器中key为1的元素个数
  cout << us.size() << endl; // 容器中元素的个数、
  us.clear(); // 清空容器
  cout << us.empty() << endl;
}

输出

1 2 5 4 3 6

1 5 4 3 6

1

5

1

3. unordered_multiset

unordered_multiset和unordered_set的唯一区别是它允许键值冗余,即可以储存key值重复的元素。因此,两种容器的find和count的意义也有所区别。

3.1 成员函数的区别

find

find 功能
unordered_set 返回key值为x的元素的迭代器
unordered_multiset 返回哈希表中第一个key值为x的元素的迭代器

count

count 功能
unordered_set 键值key为x的元素存在则返回1,不存在则返回0
unordered_multiset 返回键值key为x的元素个数

3.2 示例

void unordered_multiset_test()
{
  unordered_multiset<int> ums;
  ums.insert(1);
  ums.insert(1);
  ums.insert(2);
  ums.insert(7);
  ums.insert(5);
  ums.insert(4);
  ums.insert(2);
  for(auto e : ums)
  {
    cout << e << " ";
  }
  cout << endl;
}

输出

4 5 2 2 7 1 1

4. unordered_map

4.1 unordered_map的介绍

通过查阅官方文档们可以知道unordered_map的特性:

  • unordered_map是存储<key, value>键值对的关联式容器,其允许通过key值快速的索引到与其对应的value值;键值key通常用于唯一地标识元素,而value值是一个对象,它的内容和键值key关联;
  • unordered_map没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中;
  • unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低;
  • unordered_map实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value;
  • 它只有单向迭代器。

4.2 unordered_map的使用

unordered_map的参数列表

头文件

#include <unordered_map>

模板参数列表

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
  • Key:容器中存储元素的类型;
  • Hash:确定元素存储位置所用的哈希函数;
  • KeyEqual:判断各个元素是否相等所用的函数;
  • Allocator:指定分配器对象的类型(暂不做了解)。

unordered_map的构造器

函数声明 功能介绍
unordered_map (const Compare& comp = Compare(), const Allocator& = Allocator()); 构造空的unordered_map
unordered_map (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); 用[first, last)区 间中的元素构造 unordered_map
unordered_map (const map<Key,Compare,Allocator>& x); unordered_map的拷贝构造

示例:

void unordered_map_test()
{
  unordered_map<int, string> um1; // 构造一个键值对为<int, string>的空容器
  unordered_map<int, string> um2(um1.begin(), um1.end()); // 迭代器区间构造
  unordered_map<int, string> um3(um1); // 拷贝构造
}

unordered_map的常用接口

函数声明 功能
pair<iterator,bool> insert ( const value_type& x ) 在map中插入键值对x,注意x是一个键值对,返回值也是键值对。iterator代表新插入元素的位置,bool代表插入成功。
void erase ( iterator position ) 删除position位置的元素
size_type erase ( const key_type& x ) 删除键值key为x的元素,返回删除元素的个数
void erase ( iterator first, iterator last ) 删除[first, last)之间所有元素
iterator find ( const key_type& x ) 查找键值key为x的元素,找到则返回该元素的迭代器,否则返回end()
const_iterator find ( const key_type& x ) const 查找键值key为x的元素,找到则返回该元素的const迭代器,否则返回cend()
size_type size() const 返回unordered_map容器中有效元素的个数
bool empty ( ) const 判断unordered_map容器是否为空
void clear ( ) 清空unordered_map容器
void swap ( unordered_map<Key,T,Compare,Allocator>& mp ) 交换两个unordered_map容器的数据
size_type count ( const key_type& x ) const 返回键值key为x的元素的个数
mapped_type& operator[] (const key_type& k) 返回键值key为x对应的value

在常用的接口中,unordered_map新增了一个操作符[],它可以根据传入的key找到对应的value。

[key]表示返回key对应的value的==引用==,所以我们不仅可以通过operator[]查找,还能修改key对应的value。

  • 如果key不存在:operator[]会调用默认构造函数创建一个匿名对象用来构造一个pair,然后插入,接着才会返回它的引用;
  • 如果key存在:返回键值为key的元素对应的pair对象的引用。

迭代器相关

成员函数 功能
iterator begin() 获取容器中第一个元素的正向迭代器
iterator end() 获取容器中最后一个元素下一个位置的正向迭代器
const_iterator cbegin() 同上,但是不能修改迭代器对应的元素
const_iterator end() 同上,但是不能修改迭代器对应的元素

unordered_map没有反向迭代器。

示例

void unordered_map_test2()
{
  unordered_map<int, string> um;
  um.insert(make_pair(1, "一"));
  um.insert(make_pair(2, "二"));
  um.insert(make_pair(3, "三"));
  um.insert(make_pair(4, "四"));
  // 迭代器遍历
  unordered_map<int, string>::iterator it = um.begin();
  while (it != um.end())
  {
    cout << "<" << it->first << ", " << it->second << ">" << endl;
    it++;
  }
  cout << endl;
  // 删除key=2的元素
  um.erase(2);
  for (auto e : um)
  {
    cout << "<" << e.first << ", " << e.second << ">" << endl;
  }
  cout << endl;
  // 查找key=3的元素
  auto pos = um.find(3);
  if(pos != um.end())
  {
    cout << "<" << pos->first << ", " << pos->second << ">" << endl;
  }
  // 清空容器
  um.clear();
  // 容器判空
  cout << um.empty() << endl;
}

输出

<4, 四>

< 3, 三>

<2, 二>

<1, 一>

<4, 四>

< 3, 三>

<1, 一>

< 3, 三>

1

5. unordered_multimap

unordered_multimap容器与unordered_map容器的唯一区别就是它允许键值冗余,即unordered_multimap容器当中存储的键值对的key值是可以重复的。因此,两种容器的find和count的意义也有所区别。

5.1 成员函数的区别

find

find 功能
unordered_map 返回key值为x的元素的迭代器
unordered_multimap 返回哈希表中第一个key值为x的元素的迭代器

count

count 功能
unordered_map 键值key为x的元素存在则返回1,不存在则返回0
unordered_multimap 返回键值key为x的元素个数

5.2 示例

void unordered_multimap_test()
{
  unordered_multimap<int, string> umm;
  umm.insert(make_pair(1, "一"));
  umm.insert(make_pair(1, "first"));
  umm.insert(make_pair(1, "one"));
  for (auto e : umm)
  {
    cout << "<" << e.first << ", " << e.second << ">" << endl;
  }
  cout << endl;
}

输出

<1, 一>

<1, first>

<1, one>

目录
相关文章
|
17天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
52 18
你对Collection中Set、List、Map理解?
|
11天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
49 20
|
28天前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
30 3
【C++】map、set基本用法
|
28天前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
22 5
|
2月前
|
存储 JavaScript 前端开发
Set、Map、WeakSet 和 WeakMap 的区别
在 JavaScript 中,Set 和 Map 用于存储唯一值和键值对,支持多种操作方法,如添加、删除和检查元素。WeakSet 和 WeakMap 则存储弱引用的对象,有助于防止内存泄漏,适合特定场景使用。
|
3月前
|
存储 Java API
【数据结构】map&set详解
本文详细介绍了Java集合框架中的Set系列和Map系列集合。Set系列包括HashSet(哈希表实现,无序且元素唯一)、LinkedHashSet(保持插入顺序的HashSet)、TreeSet(红黑树实现,自动排序)。Map系列为双列集合,键值一一对应,键不可重复,值可重复。文章还介绍了HashMap、LinkedHashMap、TreeMap的具体实现与应用场景,并提供了面试题示例,如随机链表复制、宝石与石头、前K个高频单词等问题的解决方案。
47 6
【数据结构】map&set详解
|
2月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
43 1
|
3月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
42 5
|
3月前
|
存储 JavaScript 前端开发
js的map和set |21
js的map和set |21
|
3月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用