C++unordered系列

1. 序列式容器和关联式容器

1.1 序列式容器

序列式容器按照线性顺序存储元素,元素的位置取决于插入的时间和位置,与元素的值无关。

主要特点:

  • 元素按插入顺序存储

  • 可以通过位置(索引)直接访问元素

  • 不自动排序

  • 允许重复元素

常见的序列式容器:

  1. array(C++11)

    • 固定大小的数组

    • 内存连续分配

    • 大小在编译时确定

    • 快速随机访问

  2. vector

    • 动态数组

    • 内存连续分配

    • 可动态扩展

    • 在尾部插入/删除高效

    • 支持快速随机访问

  3. deque(双端队列)

    • 双端可扩展

    • 内存分段连续

    • 在头尾插入/删除高效

    • 支持快速随机访问(比vector稍慢)

  4. list

    • 双向链表

    • 内存非连续分配

    • 在任何位置插入/删除高效

    • 不支持随机访问

  5. forward_list(C++11)

    • 单向链表

    • 内存非连续分配

    • 更节省空间

    • 只支持单向遍历

1.2 关联式容器

关联式容器按照特定顺序存储元素,元素的顺序取决于元素的键(key),而不是插入的顺序。

主要特点:

  • 元素按特定顺序(平衡二叉搜索树或哈希)存储

  • 通过键(key)快速查找元素

  • 通常实现为平衡二叉搜索树或哈希表

  • 有些版本不允许重复元素

常见的关联式容器:

  1. 有序关联容器(基于红黑树实现)

    • set:唯一键的集合,按键排序

    • map:键值对集合,按键排序,键唯一

    • multiset:键可重复的set

    • multimap:键可重复的map

  2. 无序关联容器(C++11引入,基于哈希表实现)

    • unordered_set:唯一键的集合,基于哈希

    • unordered_map:键值对集合,基于哈希,键唯一

    • unordered_multiset:键可重复的unordered_set

    • unordered_multimap:键可重复的unordered_map

2. 认识pair类型

2.1 概念

pair是C++标准模板库(STL)中的一个实用模板类,用于将两个值组合成一个单一对象,可以存储两个不同类型的元素,称为firstsecond。这两个值可以是相同类型,也可以是不同类型。它定义在<utility>头文件中,是许多STL容器(如mapunordered_map)的基础构建块。

2.2 pair实现

template <class T1, class T2>
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;

    first_type first;
    second_type second;

    pair(): first(T1()), second(T2()) {}    // 默认构造

    pair(const T1& a, const T2& b): first(a), second(b) {}

    template<class U, class V>
    pair (const pair<U,V>& pr): first(pr.first), second(pr.second) {}    // 拷贝构造
};

为什么拷贝构造需要使用类中内嵌模板?

template <typename U1, typename U2> pair(const pair<U1, U2>& p); 这个构造函数的存在是为了支持跨类型的拷贝构造,而不仅仅是同类型的拷贝构造。这是 C++ 模板类设计中的一个重要特性,称为转换构造函数

  1. 类型转换支持

    • 允许从 pair<U1, U2> 构造 pair<T1, T2>

    • 只要 U1 可以转换为 T1U2 可以转换为 T2

  2. 场景:

    std::pair<int, double> p1(42, 3.14);
    std::pair<long, float> p2(p1);  // 需要这个模板构造函数
    
  3. 与普通拷贝构造的区别:

    • 普通拷贝构造:pair(const pair<T1, T2>& p)

    • 模板拷贝构造:template <typename U1, typename U2> pair(const pair<U1, U2>& p)

  4. 如果没有这个模板拷贝构造函数会怎么样?

    std::pair<int, std::string> p1(1, "hello");
    std::pair<double, const char*> p2(p1);  // 编译错误
    // 因为没有从 pair<int,string> 到 pair<double,const char*> 的转换路径
    

2.3 创建和初始化pair

2.3.1 构造函数
std::pair<int, std::string> p1(1, "apple");
2.3.2 使用make_pair函数(自动推导类型)

make_pair函数模板原型:

template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{
    return ( pair<T1,T2>(x,y) );
}
auto p2 = std::make_pair(2, "banana");
2.3.3 C++11-initializer_list初始化
std::pair<int, std::string> p3 = {3, "cherry"};

2.4 访问pair成员

通过 firstsecond 访问成员。

2.4.1 普通访问
std::pair<int, std::string> p(1, "apple");

std::cout << "First: " << p.first << std::endl;    // 输出: First: 1
std::cout << "Second: " << p.second << std::endl;  // 输出: Second: apple
2.4.2 结构化绑定(C++17)
auto p = std::make_pair(3.14, "pi");
auto [value, name] = p;  // value=3.14, name="pi"

3. 哈希表

3.1 什么是哈希表?

哈希表又称散列,是一种能够提供平均情况下近乎常数时间复杂度 O(1) 进行插入、删除和查找操作的数据结构。

它的核心思想是:通过一个函数(哈希函数)将数据(键,Key)映射到一个固定大小的数组的某个索引位置。通常的实现方式有闭散列开散列

  • 哈希表中的key需要能转换成为一个整数

  • 并且哈希表中的key需要支持 == 的比较

库中提供了对应的仿函数来实现。

3.2 负载因子

在哈希表(Hash Table)的设计与优化中,负载因子(Load Factor) 是衡量哈希表 拥挤程度 的核心指标,直接影响哈希冲突的概率和哈希表的性能。

负载因子=N/M负载因子 = N / M负载因子=N/M

  • 已存储键值对数量(N):当前哈希表中实际存放的有效数据个数。
  • 哈希表总容量(M):哈希表预设的最大存储位置数(例如数组长度)。

3.3 哈希函数

哈希函数(Hash Function)是一种将任意大小、任意类型的数据(key)映射到固定大小位置 的函数。

3.3.1 除留余数法

取关键字(Key)除以某个数 m 的余数,作为其哈希值(即哈希表的索引)。

公式:hash(key)=keymod   mhash(key)=key\mod\ mhash(key)=keymod m

  • key 是输入的关键字(通常需要先转换为一个整数)。

  • m 是数组的大小。

**黄金法则:模数 m 应该是一个质数(Prime Number),并且不能太接近 2 的幂次方。

3.4 哈希冲突

哈希冲突(Hash Collision) 是哈希表(Hash Table)等哈希技术中不可避免的现象,指的是不同的输入键(Key)经过哈希函数(Hash Function)计算后,得到了相同的哈希值(Hash Value)

针对哈希冲突,常见的解决方法可分为两大类:开放定址法链地址法

3.4.1 开放定址法(Open Addressing)

当发生哈希冲突时,通过某种探测规则在哈希表中寻找下一个空闲位置存储数据,而不是直接存在哈希值对应的位置。核心思想是 冲突后继续寻找空位
常见的探测方式包括:

  • 线性探测(Linear Probing):若哈希值为h的位置已被占用,则依次检查h+1h+2…… 直到找到空闲位置。
    • 优点:实现简单,缓存利用率高;
    • 缺点:容易产生 聚集现象(多个冲突键集中在某一区域,导致后续探测效率下降)。
  • 二次探测(Quadratic Probing):冲突时,探测位置为h+i²h-i²i为探测次数),避免线性探测的聚集问题。
    • 优点:分散冲突位置,减少聚集;
    • 缺点:可能无法探测到所有空闲位置(取决于哈希表大小)。
  • 双重哈希(Double Hashing):使用第二个哈希函数计算探测步长,步长随键变化(例如step = hash2(key)),进一步降低聚集风险。
    • 优点:冲突分布更均匀,适合大规模数据;
    • 缺点:实现复杂,需设计两个哈希函数。
3.4.2 链地址法(Chaining,又称拉链法)

将哈希值相同的键值对存储在同一个链表中,哈希表的每个位置指向一个链表的头节点。核心思想是 冲突键集中存储

  • 工作流程:
    1. 键通过哈希函数得到哈希值h,定位到哈希表的索引h
    2. 若索引h处已有数据,则将新键值对插入该位置的链表尾部(或头部);
    3. 查询时,先定位到索引h,再遍历链表查找目标键。
  • 优点:
    • 不会产生聚集现象,插入删除效率高(链表操作);
    • 哈希表利用率高,无需预留大量空闲位置。
  • 缺点:
    • 链表过长时,查询效率会退化(从 O (1) 趋近于 O (n));
    • 额外存储链表指针,内存开销略大。

实际应用:Java 的HashMap、Python 的字典(Dict)均采用链地址法,当链表长度超过阈值(默认 8)时,会将链表转为红黑树,进一步优化查询效率。

3.5 代码实现

#pragma once 
#include <algorithm>
#include <iostream>
#include <vector>
#include <utility>
#include <string>

namespace hash_bucket {
    inline unsigned long __stl_next_prime(unsigned long n)  {
        // Note: assumes long is at least 32 bits.
        static const int __stl_num_primes = 28;
        static const unsigned long __stl_prime_list[__stl_num_primes] = {
            53, 97, 193, 389, 769,
            1543, 3079, 6151, 12289, 24593,
            49157, 98317, 196613, 393241, 786433,
            1572869, 3145739, 6291469, 12582917, 25165843,
            50331653, 100663319, 201326611, 402653189, 805306457,
            1610612741, 3221225473, 4294967291
        };
        const unsigned long* first = __stl_prime_list;
        const unsigned long* last = __stl_prime_list + __stl_num_primes;
        const unsigned long* pos = std::lower_bound(first, last, n);
        return pos == last ? *(last - 1) : *pos;
    }

    template<typename Key>
    struct HashK {
        size_t operator()(const Key& key) { return (size_t)key; }
    };

    template<>
    struct HashK<std::string> {
        size_t operator()(const std::string& key) {
            size_t res = 0;
            for (auto c : key) {
                res += c * 131;
            }
            return res;
        }
    };

    template<typename Key , typename Value>
    struct HashTableNode {
        std::pair<Key , Value> data;
        HashTableNode<Key , Value>* next;
        HashTableNode(const std::pair<Key , Value>& _data) :data(_data) , next(nullptr) {}
    };

    template<typename Key>
    struct IsEqualKey {
        bool operator()(const Key& k1 , const Key& k2) {
            return k1 == k2;
        }
    };

    //  前置声明
    //  默认参数只能在声明时写,不能在定义时写
    template<typename Key , typename Value , typename HashKey = HashK<Key> , typename EqualKey = IsEqualKey<Key>>
    class HashTable;

    template<typename Key , typename Value , typename Ref , typename Ptr , typename HashKey = HashK<Key> , typename EqualKey = IsEqualKey<Key>>
    struct HashIterator {
        using HashNode = HashTableNode<Key , Value>;
        using Ht = HashTable<Key , Value , HashKey , EqualKey>;
        using Self = HashIterator<Key , Value , Ref , Ptr , HashKey , EqualKey>;
        HashNode* cur;
        Ht* ht;
        HashIterator(HashNode* node , Ht* _ht) :cur(node) , ht(_ht) {}
        // 前置++
        Self& operator++() {
            if (cur->next) {
                cur = cur->next;
            } else {
                size_t key = HashKey()(cur->data.first) % ht->table.size();
                key++;
                while (key < ht->table.size() && !ht->table[key]) {
                    key++;
                }
                if (key == ht->table.size()) {
                    cur = nullptr;
                } else {
                    cur = ht->table[key];
                }
            }
            return *this;
        }

        Ref operator* () {
            return cur->data;
        }

        Ptr operator-> () {
            return &(cur->data);
        }

        bool operator== (const Self& s) {
            return cur == s.cur;
        }
        
        bool operator!= (const Self& s) {
            return cur != s.cur;
        }
    };

    // HashKey仿函数:将key转换为无符号整数
    // EqualKey仿函数:key需要支持 == 运算符
    template<typename Key , typename Value , typename HashKey , typename EqualKey>
    class HashTable {
        // 内层友元模板,不能和外层HashTable模板参数相同
        template<typename K2 , typename V2 , typename Ref2 , typename Ptr2 , typename HK2 , typename EK2>
        friend struct HashIterator;

        using HashNode = HashTableNode<Key , Value>;
        public:
        typedef HashIterator<Key , Value , std::pair<Key , Value>& , std::pair<Key , Value>* , HashKey , EqualKey> iterator;
            // using iterator = ;
            using const_iterator = HashIterator<Key , Value , const std::pair<Key , Value>& ,  const std::pair<Key , Value>* , HashKey , EqualKey>;

            iterator begin() {
                if (n == 0) {
                    return end();
                }

                for (size_t i = 0; i < table.size(); i++) {
                    if (table[i]) {
                        return iterator(table[i], this);
                    }
                }

                return end();
            }

            iterator end() {
                return iterator(nullptr , this);
            }

            HashTable()
                :table(__stl_next_prime(0)) , n(0)
            {}

            ~HashTable() {
                for (size_t i = 0; i < table.size(); i++) {
                    HashNode* cur = table[i];
                    while (cur) {
                        HashNode* next = cur->next;
                        delete cur;
                        cur = next;
                    }
                    table[i] = nullptr;
                }
            }

            HashTable(const HashTable& ht) {
               table.resize(ht.table.size());
               n = ht.n;
               for (size_t i = 0; i < ht.table.size(); i++) {
                    HashNode* cur = ht.table[i];
                    if (!cur) {
                        table[i] = nullptr;
                        continue;
                    }
                    while (cur) {
                        HashNode* newnode = new HashNode(cur->data);
                        newnode->next = table[i];
                        table[i] = newnode;
                        cur = cur->next;
                    }
                }
            }
            
            HashTable& operator=(HashTable ht) {
                table.swap(ht.table);
                std::swap(n  , ht.n);
                return *this;
            }

            bool insert (const std::pair<Key , Value>& data) {
                if (find(HashKey()(data.first))) return false; // 存在相同元素,不插入

                // 负载因子为1时,hash需要扩容
                if (n == table.size()) {
                    std::vector<HashNode*> newTable(__stl_next_prime(table.size() + 1));
                    // 将旧表中的节点依次转移到新表
                    for(auto head : table) {
                        HashNode* cur = head;
                        while (cur) {
                            HashNode* next = cur->next;
                            size_t key = HashKey()(cur->data.first) % newTable.size();
                            cur->next = newTable[key];
                            newTable[key] = cur;
                            cur = next;
                        }
                        head = nullptr;
                    }
                    // 交换旧表和新表
                    table.swap(newTable);
                }
                // 添加新节点
                HashNode* newnode = new HashNode(data);
                size_t key = HashKey()(data.first) % table.size();
                newnode->next = table[key];
                table[key] = newnode;
                n++;

                return true;
            }

            bool erase(const Key& key) {
                size_t hashIndex = HashKey()(key) % table.size();
                HashNode* prev = nullptr;
                HashNode* cur = table[hashIndex];
                
                while (cur) {
                    // 删除需要区分头删还是中间节点删除
                    if (EqualKey()(HashKey()(cur->data.first) , HashKey()(key))) {
                        if (!prev) {
                            // 头删
                            table[hashIndex] = cur->next;
                        } else {
                            // 中间节点删除
                            prev->next = cur->next;
                        }
                        delete cur;
                        return true;
                    } else {
                        // 迭代继续寻找
                        prev = cur;
                        cur = cur->next;
                    }
                }
                return false;
            }

            bool find (const Key& key) {
                size_t hashIndex = HashKey()(key) % table.size();
                HashNode* cur = table[hashIndex];
                while (cur) {
                    if (EqualKey()(HashKey()(cur->data.first) , HashKey()(key))) return true;
                    cur = cur->next;
                }
                return false;
            }
            
        private:
            std::vector<HashNode*> table;
            size_t n;
    };

} // end namespace hash_bucket

4. unordered_set

unordered_set是C++ STL中的关联式容器,它存储唯一元素(key)并自动去重。

unordered_set原型

template < 
    class Key, 
    class Hash = hash<Key>, // unordered_set::hasher
    class Pred = equal_to<Key>, // unordered_set::key_equal
    class Alloc = allocator<Key> // unordered_set::allocator_type
    > class unordered_set;
  • Key就是unordered_set底层关键字的类型

  • unordered_set默认要求Key⽀持转换为整形,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key转成整形的仿函数传给第⼆个模板参数

  • unordered_set默认要求Key⽀持⽐较相等,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key⽐较相等的仿函数传给第三个模板参数

4.1 成员函数

4.1.1 成员类型
成员类型 解释
key_type 第一个模板参数Key
value_type 第一个模板参数Key
4.1.2 构造函数
序号 函数原型 说明
1️⃣ explicit unordered_set ( size_type n = / see below /, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() ) 默认构造
2️⃣ unordered_set ( const unordered_set& ust ) 拷贝构造
3️⃣ unordered_set ( initializer_list<value_type> il, size_type n = / see below /, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() ) 使用 initializer_list 初始化
4️⃣ template <class InputIterator> unordered_set ( 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() ) 使用一段迭代器区间初始化
  • n = /* see below */:第一个参数有默认值(具体值由实现定义,通常是某个初始桶数)。
4.1.3 赋值重载
序号 函数原型 说明
1️⃣ unordered_set& operator= ( const unordered_set& ust ) 两个已存在的 unordered_set 对象的赋值
2️⃣ unordered_set& operator= ( intitializer_list<value_type> il ) 使用 initializer_list 赋值
4.1.4 迭代器
序号 函数原型 说明
1️⃣ iterator begin() 返回指向 unordered_set 对象中第一个元素的迭代器
2️⃣ const_iterator begin() const 返回指向 unordered_set 对象中第一个元素的 const 迭代器
3️⃣ iterator end() 返回指向 unordered_set 对象末尾元素之后位置的迭代器
4️⃣ const_iterator end() const 返回指向 unordered_set 对象末尾元素之后位置的 const 迭代器

unordered_系列是单向迭代器。

4.1.5 容量相关的接口
序号 函数原型 说明
1️⃣ bool empty() const 判断 unordered_set 对象是否为空
2️⃣ size_type size() const 返回 unordered_set 对象中元素的数量
4.1.6 修改相关的接口
序号 函数原型 说明
1️⃣ pair<iterator,bool> insert (const value_type& val) 向 unordered_set 对象中插入 val 元素
2️⃣ iterator erase (const_iterator position) 删除 unordered_set 对象中 position 迭代器位置元素,返回删除元素的下一个有效迭代器
3️⃣ size_type erase (const key_type& val) 删除 unordered_set 对象中 val 元素,返回值返回为删除的 val 元素(0或1)
4️⃣ void clear() 清空 unordered_set 对象

pair<iterator,bool> insert (const value_type& val) 返回值解析

返回值是一个 std::pair,包含两个部分:

  1. iterator:指向插入元素的迭代器

    • 如果插入成功:指向新插入的元素

    • 如果插入失败(元素已存在):指向已存在的元素

  2. bool:表示插入是否成功

    • true:元素被成功插入

    • false:元素已存在,未插入新元素

4.1.7 其他
序号 函数原型 说明
1️⃣ iterator find (const value_type& val) 在 unordered_set 对象中查找 val 元素,成功返回该位置迭代器,失败返回迭代器end()
2️⃣ size_type count (const value_type& val) const 返回 unordered_set 对象中 val 元素的数量(0或1)

4.2 unordered_set与unordered_multiset的区别

unordered_set和unordered_multiset都是C++ STL中的关联容器,它们的主要区别在于元素的唯一性。

主要区别

  1. 元素唯一性

    • unordered_set:存储唯一元素,不允许重复

    • unordered_multiset:允许存储重复元素

  2. 插入操作

    • unordered_set插入已存在元素时,插入操作会失败

    • unordered_multiset可以重复插入相同元素

  3. count() 返回值

    • unordered_set:只能是 0 或 1

    • unordered_multiset:可以是 0, 1, 或任意正整数

  4. erase() 返回值

    • unordered_set:删除的元素个数(0 或 1)

    • unordered_multiset:删除的所有该元素的总个数

  • unordered_set与unordered_multiset接口一致。

5. unordered_map

unordered_map 是C++标准模板库(STL)中的一个关联容器,它存储的元素是pair类型,并且根据键(key)自动去重。

unordered_map原型

template < class Key, class T, class Hash = hash<Key>,  class Pred = equal_to<Key>, class Alloc = allocator<pair<const Key,T>>> class unordered_map;

unordered_map中存储的pair类型

typedef pair<const Key, T> value_type;
  • Key就是map底层关键字的类型,T是map底层value的类型

  • unordered_map默认要求Key⽀持转换为整形,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key转成整形的仿函数传给第三个模板参数

  • unordered_map默认要求Key⽀持⽐较相等,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key⽐较相等的仿函数传给第四个模板参数

5.1 成员函数

5.1.1 成员类型
成员类型 解释
key_type 第一个模板参数Key
mapped_type 第二个模板参数T
5.1.2 构造函数
序号 函数原型 说明
1️⃣ explicit unordered_map ( size_type n = / see below /, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() );explicit unordered_map ( const allocator_type& alloc ) 默认构造
2️⃣ unordered_map ( const unordered_map& ump ) 拷贝构造
3️⃣ unordered_map ( initializer_list<value_type> il, size_type n = / see below /, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type() ) 使用 initializer_list 初始化
4️⃣ 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() ) 使用一段迭代器区间初始化
  • n = /* see below */:第一个参数有默认值(具体值由实现定义,通常是某个初始桶数)。
5.1.3 赋值重载
序号 函数原型 说明
1️⃣ unordered_map& operator= ( const unordered_map& ump ) 两个已存在的 unordered_map 对象的赋值
2️⃣ unordered_map& operator= ( intitializer_list<value_type> il ) 使用 initializer_list 赋值
5.1.4 迭代器
序号 函数原型 说明
1️⃣ iterator begin() 返回指向 unordered_map 对象中第一个元素的迭代器
2️⃣ const_iterator begin() const 返回指向 unordered_map 对象中第一个元素的 const 迭代器
3️⃣ iterator end() 返回指向 unordered_map 对象末尾元素之后位置的迭代器
4️⃣ const_iterator end() const 返回指向 unordered_map 对象末尾元素之后位置的 const 迭代器

unordered_系列是单向迭代器。

5.1.5 容量相关的接口
序号 函数原型 说明
1️⃣ bool empty() const 判断 unordered_map 对象是否为空
2️⃣ size_type size() const 返回 unordered_map 对象中元素的数量
5.1.6 元素的访问
序号 函数原型
1️⃣ mapped_type& operator[] (const key_type& k)

unordered_map 的 operator[] 是一个非常有用的成员函数,它提供了对映射值的快速访问和修改能力。

功能说明

  1. 查找与修改:如果键 k 存在于 map 中,返回对应的映射值(value)的引用

  2. 插入与修改:如果键 k 不存在,会自动插入一个新的键值对,键为 k,值为 mapped_type 的默认构造值,然后返回这个新值(value)的引用

operator[]实现

(*( (insert(make_pair(k,mapped_type()))).first).second)

解析

mapped_type& operator[] (const key_type& k) {
    pair<iterator, bool> ret = insert({ k, mapped_type() });
    iterator it = ret.first;
    return it->second;
}
5.1.7 修改相关的接口
序号 函数原型 说明
1️⃣ pair<iterator,bool> insert (const value_type& val) 向 unordered_map 对象中插入 val 元素
2️⃣ iterator erase (const_iterator position) 删除 unordered_map 对象中 position 迭代器位置元素,返回删除元素的下一个有效迭代器
3️⃣ size_type erase (const key_type& val) 删除 unordered_map 对象中 val 元素,返回值返回为删除的 val 元素(0或1)
4️⃣ void clear() 清空 unordered_map  对象

pair<iterator,bool> insert (const value_type& val) 返回值解析

返回值是一个 std::pair,包含两个部分:

  1. iterator:指向插入元素的迭代器

    • 如果插入成功:指向新插入的元 素

    • 如果插入失败(元素已存在):指向已存在的元素

  2. bool:表示插入是否成功

    • true:元素被成功插入

    • false:元素已存在,未插入新元素

5.1.8 其他
序号 函数原型 说明
1️⃣ iterator find (const value_type& val) 在 unordered_map 对象中查找 val 元素,成功返回该位置迭代器,失败返回迭代器end()
2️⃣ size_type count (const value_type& val) const 返回 unordered_map 对象中 val 元素的数量(0或1)

5.2 unordered_map与unordered_multimap的区别

unordered_map和unordered_multimap都是C++ STL中的关联容器,它们的主要区别在于元素的唯一性。

主要区别

  1. 元素唯一性

    • unordered_map:存储唯一元素,不允许重复

    • unordered_multimap:允许存储重复元素

  2. 插入操作

    • unordered_map插入已存在元素时,插入操作会失败

    • unordered_multimap可以重复插入相同元素

  3. operator[]

    • unordered_map:支持 operator[] 访问

    • unordered_multimap:不支持 operator[],因为键不唯一

  • map与multimap接口一致。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐