C++ -- 学习系列 无序关联式容器 unordered_set 与 unordered_map(未完待续)

一    Hash Table 是什么?

   哈希表(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++ HashTable 结构、底层源码与使用?

C++那些事之彻底搞懂STL HashTable - 知乎 (zhihu.com)

 1. 结构图

    底层是一个数组,数组中每个索引代表一个 bucket,元素实际存储在链表上,若是出现 哈希冲突,新元素定位在一个 已有元素的 bucket 上,则将新元素 append 到bucket 的链表上。

2. 源码

   这里只列出了_Hashtable 的模板参数部分

 template<typename _Key, typename _Value, typename _Alloc,
	   typename _ExtractKey, typename _Equal,
	   typename _H1, typename _H2, typename _Hash,
	   typename _RehashPolicy, typename _Traits>
    class _Hashtable

     模板参数含义:

  参数含义
_Key关联容器的键类型
_Value关联容器的值类型
_Alloc用于内存分配的分配类型
_ExtractKey从值中提取键的函数对象类型
_Equal判断键是否相等的函数对象类型
_H1第一个 Hash 的函数对象类型,用于计算关键字的 hashcode 
_H2第二个 Hash 的函数对象类型,用于计算 hashcode 对应的bucket 索引
_HashHash 函数对象类型
_RehashPolicy重新哈希函数策略类型
_Traits特定容器特定类型

            常见函数解析:

1.1 find

 template<typename _Key, typename _Value,
	   typename _Alloc, typename _ExtractKey, typename _Equal,
	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
	   typename _Traits>
    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 节点的 迭代器

1.2  模板参数中_H1 与 _H2 的区别

   _H1 是 是计算 hash code 时用的函数对象,_H2 是 通过 hashcode 计算 桶的索引的函数对象。

 源代码:

     奥秘在于 struct _Hash_code_base 

template<typename _Key, typename _Value, typename _ExtractKey,
	   typename _H1, typename _H2>
    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<typename _Key, typename _Value,
	   typename _ExtractKey, typename _Equal,
	   typename _H1, typename _H2, typename _Hash, typename _Traits>
  struct _Hashtable_base
  : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
			   _Traits::__hash_cached::value>,
    ....
{
  .....
};

 template<typename _Key, typename _Value, typename _Alloc,
	   typename _ExtractKey, typename _Equal,
	   typename _H1, typename _H2, typename _Hash,
	   typename _RehashPolicy, typename _Traits>
 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 函数对象

1.3  _Hash_node 结构

 _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<typename _Value, bool _Cache_hash_code>
    struct _Hash_node;

  /**
   *  Specialization for nodes with caches, struct _Hash_node.
   *
   *  Base class is __detail::_Hash_node_value_base.
   */
  template<typename _Value>
    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<typename _Value>
    struct _Hash_node<_Value, false> : _Hash_node_value_base<_Value>
    {
      _Hash_node*
      _M_next() const noexcept
      { return static_cast<_Hash_node*>(this->_M_nxt); }
    };

2.  使用

  2.1  hashtable 当作set 使用

#include<iostream>
#include <bits/hashtable.h>

int main()
{
    std::_Hashtable<int, int,  std::allocator<int>, std::_Identity<int>,
             std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
             std::__detail::_Hashtable_traits<std::__cache_default<int, std::hash<int>>::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<int, int,  std::allocator<int>, std::_Identity<int>,
             std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
             std::__detail::_Hashtable_traits<std::__cache_default<int, std::hash<int>>::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<bool _Cache_hash_code, bool _Constant_iterators, bool _Unique_keys>
    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>;
    };

 2.2 hashtable 当作 map 使用

#include<iostream>
#include<string>
#include <bits/hashtable.h>

int main()
{
   // 1. key 唯一
    std::_Hashtable<int, std::pair<int, std::string>,  std::allocator<int>, std::__detail::_Select1st,
            std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
            std::__detail::_Hashtable_traits<std::__cache_default<int, std::hash<int>>::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<int, std::pair<int, std::string>,  std::allocator<int>, std::__detail::_Select1st,
            std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy,
            std::__detail::_Hashtable_traits<std::__cache_default<int, std::hash<int>>::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;
}

输出:

三  无序关联式容器的底层原理

1. set

 1.1  unordered_set 源码


/// Base types for unordered_set.
  template<bool _Cache>
    using __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>;

  template<typename _Value,
	   typename _Hash = hash<_Value>,
	   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 _Value,
	   class _Hash = hash<_Value>,
	   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是相同的
_Hashhash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash<Key>
_Pred与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to<Key>
_Alloc用于内存分配的分配器类型, 默认是 allocator<_Value>

1.2  unordered_multiset 源码

/// Base types for unordered_multiset.
  template<bool _Cache>
    using __umset_traits = __detail::_Hashtable_traits<_Cache, true, false>;

  template<typename _Value,
	   typename _Hash = hash<_Value>,
	   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 _Value,
	   class _Hash = hash<_Value>,
	   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是相同的
_Hashhash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash<Key>
_Pred与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to<Key>
_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 的类型是同一种,

2. map

 2.1  unordered_map 源码

/// Base types for unordered_map.
  template<bool _Cache>
    using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;

  template<typename _Key,
	   typename _Tp,
	   typename _Hash = hash<_Key>,
	   typename _Pred = std::equal_to<_Key>,
	   typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,
	   typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
    using __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
                                        _Alloc, __detail::_Select1st,
				        _Pred, _Hash,
				        __detail::_Mod_range_hashing,
				        __detail::_Default_ranged_hash,
				        __detail::_Prime_rehash_policy, _Tr>;

 template<class _Key, class _Tp,
	   class _Hash = hash<_Key>,
	   class _Pred = std::equal_to<_Key>,
	   class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
    class unordered_map
    {
      typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
      _Hashtable _M_h;
    ......
};

       class unordered_map 类模板参数解析:

参数名说明
_Key容器键类型
_Tp容器值类型
_Hashhash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash<Key>
_Pred与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to<Key>
_Alloc用于内存分配的分配器类型, 默认是 allocator<pair<const Key, _Tp>>

2.2 unoredred_multimap 源码

/// Base types for unordered_multimap.
  template<bool _Cache>
    using __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>;

  template<typename _Key,
	   typename _Tp,
	   typename _Hash = hash<_Key>,
	   typename _Pred = std::equal_to<_Key>,
	   typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,
	   typename _Tr = __ummap_traits<__cache_default<_Key, _Hash>::value>>
    using __ummap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
					 _Alloc, __detail::_Select1st,
					 _Pred, _Hash,
					 __detail::_Mod_range_hashing,
					 __detail::_Default_ranged_hash,
					 __detail::_Prime_rehash_policy, _Tr>;

template<class _Key, class _Tp,
	   class _Hash = hash<_Key>,
	   class _Pred = std::equal_to<_Key>,
	   class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
    class unordered_multimap
    {
      typedef __ummap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
      _Hashtable _M_h;
   ......
};

class unordered_multimap 类模板参数解析:

参数名说明
_Key容器键类型
_Tp容器值类型
_Hashhash 函数,与 hashtable 的_H1 对应,用于将键转为 hashcode,默认是 hash<Key>
_Pred与 hashtable 的 _Equal 对应,该函数对象类型用于判断两个键之间是否相等, 默认是 equal_to<Key>
_Alloc用于内存分配的分配器类型, 默认是 allocator<pair<const Key, _Tp>>

四  无序关联式容器的使用

1. set

 1.1  unordered_set 常见函数与使用

std::unordered_set - cppreference.com

   1.1.1  构造函数
函数说明
unordered_set空构造函数
template< class InputIt >

unordered_set( InputIt first, InputIt last)

构造拥有范围 [first, last) 的内容的容器
unordered_set( size_type bucket_count)指定 bucket 数量的构造函数
unordered_set(initializer_list<value_type> __l)
通过 
initializer_list构造 set的构造函数

1.1.2  迭代器
函数说明
begin 返回 容器中第一个元素的 iterator
end返回 容器中最后一个元素的下一个 iterator
1.1.3  容器容量
函数说明
empty判断容器是否为空
size返回容器存储的元素数量
1.1.4  容器修改
函数说明
clear清空容器
insert向容器插入元素
emplace向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造
erase移除指定元素或者指定 位置的元素
1.1.5  容器查询
函数说明
count返回指定元素的数量
find返回指定元素的 iterator
contains返回容器中是否存在指定元素 (c++ 20 支持)

示例代码:

#include<iostream>
#include<unordered_set>

int main()
{
    std::vector<int> vec = {1, 2, 3, 4, 5, 6};

    // vec.begin()
    // 1. 初始化构造函数
    std::unordered_set<int>  set1;
    std::unordered_set<int>  set2(vec.begin(), vec.end(), 7);
    std::unordered_set<int>  set3 = {1, 2, 3, 4, 5, 6};
    std::unordered_set<int>  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;
}

输出:

 1.2  unordered_multiset 常见函数与使用

 1.2.1  构造函数
函数说明
unordered_set空构造函数
template< class InputIt >

unordered_set( InputIt first, InputIt last)

构造拥有范围 [first, last) 的内容的容器
unordered_set( size_type bucket_count)指定 bucket 数量的构造函数
unordered_set(initializer_list<value_type> __l)
通过 
initializer_list构造 set的构造函数
  1.2.2  迭代器
函数说明
begin 返回 容器中第一个元素的 iterator
end返回 容器中最后一个元素的下一个 iterator
1.2.3  容器容量
函数说明
empty判断容器是否为空
size返回容器存储的元素数量
1.2.4  容器修改
函数说明
clear清空容器
insert向容器插入元素
emplace向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造
erase移除指定元素或者指定 位置的元素
1.2.5  容器查询
函数说明
count返回指定元素的数量
find返回指定元素的 iterator
contains返回容器中是否存在指定元素 (c++ 20 支持)

示例代码:

#include<unordered_map>
#include<iostream>

int  main()
{
    std::vector<int> vec = {1, 2, 3, 3, 3, 4, 5, 6};

    // vec.begin()
    // 1. 初始化构造函数
    std::unordered_multiset<int>  set1;
    std::unordered_multiset<int>  set2(vec.begin(), vec.end(), 7);
    std::unordered_multiset<int>  set3 = {1, 2, 3, 3, 3, 4, 5, 6};
    std::unordered_multiset<int>  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;
}

输出:

2. map

 2.1  unordered_map 常见函数与使用

 2.1.1  构造函数
函数说明
unordered_map空构造函数
template< class InputIt >

unordered_map( InputIt first, InputIt last)

构造拥有范围 [first, last) 的内容的容器
unordered_map( std::initializer_list<value_type> init)通过 list 来构造 map 的gou'z
  2.1.2  迭代器
函数说明
begin 返回 容器中第一个元素的 iterator
end返回 容器中最后一个元素的下一个 iterator
2.1.3  容器容量
函数说明
empty判断容器是否为空
size返回容器存储的元素数量
2.1.4  容器修改
函数说明
clear清空容器
insert向容器插入元素
emplace向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造
erase移除指定元素或者指定 位置的元素
2.1.5  容器查询
函数说明
count返回指定 key 的数量
find返回指定 key 的 iterator
contains返回容器中是否存在指定元素 (c++ 20 支持)
at返回指定 key 的引用
operator[]返回指定 key 的引用,若是不存在该 key, 则将 key 与对应的值插入容器

示例代码:

#include<iostream>
#include<unordered_map>

int main()
{
 // 1. 初始化构造函数
    std::unordered_map<int, std::string>  map1;
    std::unordered_map<int, std::string>  map2 = {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}, {5, "e"}};
    std::unordered_map<int, std::string>  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) <<std::endl;

    return 0;
}

输出:

2.2 unoredred_multimap 常见函数与使用

 2.2.1  构造函数
函数说明
unordered_map空构造函数
template< class InputIt >

unordered_map( InputIt first, InputIt last)

构造拥有范围 [first, last) 的内容的容器
unordered_map( std::initializer_list<value_type> init)通过 list 来构造 map 的gou'z
  2.2.2  迭代器
函数说明
begin 返回 容器中第一个元素的 iterator
end返回 容器中最后一个元素的下一个 iterator
2.2.3  容器容量
函数说明
empty判断容器是否为空
size返回容器存储的元素数量
2.2.4  容器修改
函数说明
clear清空容器
insert向容器插入元素
emplace向容器插入元素,该函数与 insert的区别在于,该函数可以只传入元素类的构造参数实现元素的原地构造
erase移除指定元素或者指定 位置的元素
2.2.5  容器查询
函数说明
count返回指定 key 的数量
find返回指定 key 的 iterator
contains返回容器中是否存在指定元素 (c++ 20 支持)

注意:

没有看错,unordered_multimap 不支持 at 与 operator[] 函数访问元素,因为这个容器是允许重复 key存在的,所以你打算调用 at 与 operator[] 是访问重复key 的哪个值呢?

基于此,c++ 标准针对 multimap 就不提供 at 与 operator[] 函数 了

示例代码:

#include<iostream>
#include<unordered_multimap>

int main()
{
     // 1. 初始化构造函数
    std::unordered_multimap<int, std::string>  map1;
    std::unordered_multimap<int, std::string>  map2 = {{1, "a"}, {2, "b"}, {3, "c1"}, {3, "c2"},{3, "c3"},{4, "d"}, {5, "e"}};
    std::unordered_multimap<int, std::string>  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;
}

输出:

五 简单实现

1. set

 1.1  unordered_set 

// my_unordered_set.h

#include <bits/hashtable.h>
#include<initializer_list>
#include<memory>
#include<type_traits>

template<typename _Value>
class my_unordered_set
{
public:
    template<typename _Tp, typename _Hash>
      using __cache_default
        =  std::__not_<std::__and_<// Do not cache for fast hasher.
                 std::__is_fast_hash<_Hash>,
                 // 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<typename ..._Args>
    void emplace(_Args& ...args)
    {
        hash_container.emplace(std::forward<_Args>(args)...);
    }

    template<typename ..._Args>
    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<iostream>
#include"my_unordered_set.h"

int main()
{
        // 1. 初始化构造函数
    my_unordered_set<int>  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;
}

输出:

 1.2  unordered_multiset

// my_unordered_multiset.h

#include <bits/hashtable.h>
#include<initializer_list>
#include<memory>
#include<type_traits>

template<typename _Value>
class my_unordered_multiset
{
public:
    template<typename _Tp, typename _Hash>
      using __cache_default
        =  std::__not_<std::__and_<// Do not cache for fast hasher.
                 std::__is_fast_hash<_Hash>,
                 // 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<typename ..._Args>
    void emplace(_Args& ...args)
    {
        hash_container.emplace(std::forward<_Args>(args)...);
    }

    template<typename ..._Args>
    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<iostream>
#include"my_unordered_multiset.h"

int main()
{

  // 1. 初始化构造函数
    my_unordered_multiset<int>  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;
}

输出:

2. map

 2.1  unordered_map

// my_unordered_map.h
#include <bits/hashtable.h>
#include<initializer_list>
#include<memory>
#include<type_traits>

template<typename Key, typename Value>
class my_unordered_map
{
public:
    template<typename _Tp, typename _Hash>
      using __cache_default
        =  std::__not_<std::__and_<// Do not cache for fast hasher.
                 std::__is_fast_hash<_Hash>,
                 // Mandatory to have erase not throwing.
                 std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
    using  _traits = std::__detail::_Hashtable_traits<__cache_default<Key, std::hash<Key>>::value, false, true>;
    using _HashTable =  std::_Hashtable<Key, std::pair<Key, Value>,  std::allocator<Key>, std::__detail::_Select1st,
            std::equal_to<Key>, std::hash<Key>, 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<value_type> list)
    {
        _hash_container.insert(list);
    }

    void insert(value_type&& value)
    {
        _hash_container.insert(value);
    }

    template<typename ... _Args>
    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<iostream>
#include"my_unordered_map.h"

int main()
{
       // 1. 初始化构造函数
    my_unordered_map<int, std::string>  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) <<std::endl;

    map1.clear();
    std::cout << "empty: " << map1.empty() << std::endl;
    std::cout << "size: " << map1.size() << std::endl;

    return 0;
}

输出:

2.2 unoredred_multimap

// my_unordered_multimap.h

#include <bits/hashtable.h>
#include<initializer_list>
#include<memory>
#include<type_traits>

template<typename Key, typename Value>
class my_unordered_multimap
{
public:
    template<typename _Tp, typename _Hash>
      using __cache_default
        =  std::__not_<std::__and_<// Do not cache for fast hasher.
                 std::__is_fast_hash<_Hash>,
                 // Mandatory to have erase not throwing.
                 std::__detail::__is_noexcept_hash<_Tp, _Hash>>>;
    using  _traits = std::__detail::_Hashtable_traits<__cache_default<Key, std::hash<Key>>::value, false, false>;
    using _HashTable =  std::_Hashtable<Key, std::pair<Key, Value>,  std::allocator<Key>, std::__detail::_Select1st,
            std::equal_to<Key>, std::hash<Key>, 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<value_type> list)
    {
        _hash_container.insert(list);
    }

    void insert(value_type&& value)
    {
        _hash_container.insert(value);
    }

    template<typename ... _Args>
    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<iostream>
#include"my_unordered_multimap.h"

int main()
{
     // 1. 初始化构造函数
    my_unordered_multimap<int, std::string>  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;
}

输出:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值