C++ unordered_map和unordered_set的使用(模拟实现)

unordered_set

unordered_set是C++标准库提供的一种无序关联容器,其底层实现基于哈希表。与传统的set相比,它提供了更高效的查找性能,平均时间复杂度为O(1)。

类模板声明解析:

template < 
    class Key,                   // 元素类型
    class Hash = hash<Key>,      // 哈希函数对象
    class Pred = equal_to<Key>,  // 键值相等比较函数
    class Alloc = allocator<Key> // 内存分配器
> class unordered_set;

与set的关键差异

  1. 底层数据结构

    • set:基于红黑树(平衡二叉搜索树)
    • unordered_set:基于哈希表
  2. 元素排序

    • set:元素自动排序(按Key升序)
    • unordered_set:元素无序存储
  3. 性能特点

    • set:查找、插入、删除操作的时间复杂度为O(logN)
    • unordered_set:平均时间复杂度为O(1),最坏情况下为O(N)
  4. 迭代器特性

    • set:双向迭代器,支持前后移动
    • unordered_set:单向迭代器,只能向前移动

性能对比实验分析

两种容器在不同操作上的时间差异:

#include 
#include 
#include 
#include 
#include 
using namespace std;

void test_performance() {
    const size_t N = 100000; // 测试数据量
    
    // 生成测试数据
    vector<int> v;
    v.reserve(N);
    srand(time(0));
    for (size_t i = 0; i < N; ++i) {
        v.push_back(rand()); // 随机数据,可能有重复
    }

    // 测试set
    set<int> s;
    clock_t begin1 = clock();
    for (auto e : v) s.insert(e);
    clock_t end1 = clock();
    cout << "set插入耗时: " << (end1 - begin1) * 1000 / CLOCKS_PER_SEC << " ms" << endl;

    // 测试unordered_set
    unordered_set<int> us;
    us.reserve(N); // 预分配空间提升性能
    clock_t begin2 = clock();
    for (auto e : v) us.insert(e);
    clock_t end2 = clock();
    cout << "unordered_set插入耗时: " << (end2 - begin2) * 1000 / CLOCKS_PER_SEC << " ms" << endl;

    // 测试查找性能
    int found1 = 0, found2 = 0;
    
    clock_t begin3 = clock();
    for (auto e : v) if (s.find(e) != s.end()) ++found1;
    clock_t end3 = clock();
    cout << "set查找耗时: " << (end3 - begin3) * 1000 / CLOCKS_PER_SEC << " ms (找到" << found1 << "个)" << endl;

    clock_t begin4 = clock();
    for (auto e : v) if (us.find(e) != us.end()) ++found2;
    clock_t end4 = clock();
    cout << "unordered_set查找耗时: " << (end4 - begin4) * 1000 / CLOCKS_PER_SEC << " ms (找到" << found2 << "个)" << endl;

    // 测试删除性能
    clock_t begin5 = clock();
    for (auto e : v) s.erase(e);
    clock_t end5 = clock();
    cout << "set删除耗时: " << (end5 - begin5) * 1000 / CLOCKS_PER_SEC << " ms" << endl;

    clock_t begin6 = clock();
    for (auto e : v) us.erase(e);
    clock_t end6 = clock();
    cout << "unordered_set删除耗时: " << (end6 - begin6) * 1000 / CLOCKS_PER_SEC << " ms" << endl;
}

int main() {
    test_performance();
    return 0;
}

C++ unordered_map和unordered_set的使用(模拟实现)_第1张图片

实验结果分析

  1. 插入操作:unordered_set通常快于set,特别是数据量大时差异更明显
  2. 查找操作:unordered_set的优势最为显著
  3. 删除操作:unordered_set同样表现更优

unordered_map

unordered_map与map的差异

unordered_map与map的差异类似于unordered_set与set的差异:

  1. 键值要求

    • map:要求Key支持小于比较
    • unordered_map:要求Key可转换为整型且支持等于比较
  2. 迭代器特性

    • map:双向迭代器,遍历时按键值有序
    • unordered_map:单向迭代器,遍历时无序
  3. 性能差异

    • 与set/unordered_set类似,unordered_map在大多数场景下性能更优

允许重复键值的变体

标准库还提供了允许键值重复的版本:

  • unordered_multiset:允许重复元素的unordered_set
  • unordered_multimap:允许重复键的unordered_map

这些变体与普通版本的差异主要体现在:

  1. 允许键值重复
  2. 相关接口返回的可能是多个元素
  3. 性能特征与普通版本类似

高级特性:哈希相关接口

unordered_xxx系列容器提供了一些与哈希策略相关的接口,包括:

  • bucket_count():返回桶的数量
  • load_factor():返回当前负载因子
  • max_load_factor():获取或设置最大负载因子
  • rehash():重新哈希,调整桶的数量

这些接口在日常使用中较少涉及,但在需要精细控制哈希表性能时非常有用。

使用建议

  1. 选择容器时的考虑因素

    • 需要元素有序:选择set/map
    • 追求最高性能:优先考虑unordered_set/unordered_map
    • 需要键值重复:选择multi版本
  2. 性能优化建议

    • 对于unordered_xxx容器,预先调用reserve()可以避免多次rehash
    • 自定义类型作为Key时,需要提供良好的哈希函数
  3. 使用场景推荐

    • 高频查找操作:unordered_xxx系列
    • 需要有序遍历:set/map系列
    • 数据量较小:差异不明显,可根据代码清晰度选择

unordered_map 和 unordered_set 模拟实现

HashTable.h

#pragma once
#include

// 获取大于等于n的下一个质数
inline unsigned long __stl_next_prime(unsigned long n)
{
    // 假设long至少32位
    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 = lower_bound(first, last, n);
    return pos == last ? *(last - 1) : *pos;
}

// 哈希函数模板
template<class K>
struct HashFunc
{
    size_t operator()(const K& key)
    {
        return (size_t)key;
    }
};

// 字符串特化的哈希函数
template<>
struct HashFunc<string>
{
    size_t operator()(const string& s)
    {
        // BKDR哈希算法
        size_t hash = 0;
        for (auto ch : s)
        {
            hash += ch;
            hash *= 131;
        }
        return hash;
    }
};

namespace hash_bucket
{
    // 哈希节点模板
    template<class T>
    struct HashNode
    {
        T _data;            // 存储的数据
        HashNode<T>* _next; // 指向下一个节点的指针

        HashNode(const T& data)
            :_data(data)
            , _next(nullptr)
        {}
    };

    // 前置声明
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable;

    // 哈希表迭代器
    template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
    struct HTIterator
    {
        typedef HTIterator<K,T, Ref, Ptr, KeyOfT, Hash> Self;
        typedef HashNode<T> Node;
        typedef HashTable<K, T, KeyOfT, Hash> HT;

        Node* _node;    // 当前节点指针
        const HT* _ht; // 所属哈希表指针

        HTIterator(Node* node, const HT* ht)
            :_node(node)
            ,_ht(ht)
        {}

        // 解引用操作符
        Ref operator*()
        {
            return _node->_data;
        }

        // 箭头操作符
        Ptr operator->()
        {
            return &_node->_data;
        }

        // 不等于操作符
        bool operator!=(const Self& s)
        {
            return _node != s._node;
        }

        // 前置++操作符
        Self& operator++()
        {
            if (_node->_next)
            {
                // 当前桶还有数据,移动到下一个节点
                _node = _node->_next;
            }
            else
            {
                // 当前桶走完了,找下一个有数据的桶
                KeyOfT kot;
                Hash hash;
                size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
                ++hashi;
                while (hashi < _ht->_tables.size())
                {
                    _node = _ht->_tables[hashi];
                    if (_node)
                        break;
                    else
                        ++hashi;
                }
                if (hashi == _ht->_tables.size()) // 走到结尾了
                {
                    _node = nullptr; // end()是空标识
                }
            }
            return *this;
        }
    };

    // 哈希表模板
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable
    {
        template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
        friend struct HTIterator;
        
        typedef HashNode<T> Node;
    public:
        // 迭代器类型定义
        typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
        typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> Const_Iterator;

        // 获取起始迭代器
        Iterator Begin()
        {
            if(_n == 0)
            {
                return End();
            }
            for (size_t i = 0; i < _tables.size(); i++)
            {
                Node* cur = _tables[i];
                if (cur)
                    return Iterator(cur, this);
            }
            return End();
        }

        // 获取结束迭代器
        Iterator End() 
        {
            return Iterator(nullptr, this);
        }

        // 获取常量起始迭代器
        Const_Iterator Begin() const
        {
            if (_n == 0)
            {
                return End();
            }
            for (size_t i = 0; i < _tables.size(); i++)
            {
                Node* cur = _tables[i];
                if (cur)
                    return Const_Iterator(cur, this);
            }
            return End();
        }

        // 获取常量结束迭代器
        Const_Iterator End() const
        {
            return Const_Iterator(nullptr, this);
        }

        // 构造函数
        HashTable()
            :_tables(__stl_next_prime(0))
            , _n(0)
        {}

        // 析构函数
        ~HashTable()
        {
            for (size_t i = 0; i < _tables.size(); i++)
            {
                Node* cur = _tables[i];
                while (cur)
                {
                    Node* next = cur->_next;
                    delete cur;
                    cur = next;
                }
                _tables[i] = nullptr;
            }
        }

        // 插入元素
        pair<Iterator,bool> Insert(const T& data)
        {
            Hash hash;
            KeyOfT kot;
            Iterator it = Find(kot(data));
            if (it != End())
            {
                return { it,false }; // 元素已存在
            }

            // 负载因子 == 1时扩容
            if (_n == _tables.size())
            {
                vector<Node*> newTable(__stl_next_prime(_tables.size()+1));
                for (size_t i = 0; i < _tables.size(); i++)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        Node* next = cur->_next;
                        // 头插到新表
                        size_t hashi = hash(kot(cur->_data)) % newTable.size();
                        cur->_next = newTable[hashi];
                        newTable[hashi] = cur;
                        cur = next;
                    }
                    _tables[i] = nullptr;
                }
                _tables.swap(newTable);
            }

            size_t hashi = hash(kot(data)) % _tables.size();
            // 头插
            Node* newnode = new Node(data);
            newnode->_next = _tables[hashi];
            _tables[hashi] = newnode;
            ++_n;

            return { Iterator(newnode,this),true };
        }

        // 查找元素
        Iterator Find(const K& key)
        {
            KeyOfT kot;
            Hash hash;
            size_t hashi = hash(key) % _tables.size();
            Node* cur = _tables[hashi];
            while (cur)
            {
                if (kot(cur->_data) == key)
                {
                    return Iterator(cur,this);
                }
                cur = cur->_next;
            }
            return End();
        }

        // 删除元素
        bool Erase(const K& key)
        {
            KeyOfT kot;
            Hash hash;
            size_t hashi = hash(key) % _tables.size();
            Node* prev = nullptr;
            Node* cur = _tables[hashi];
            while (cur)
            {
                if (kot(cur->_data) == key)
                {
                    // 删除头节点
                    if (prev == nullptr)
                    {
                        _tables[hashi] = cur->_next;
                    }
                    // 删除中间节点
                    else
                    {
                        prev->_next = cur->_next;
                    }
                    delete cur;
                    --_n;
                    return true;
                }
                else
                {
                    prev = cur;
                    cur = cur->_next;
                }
            }
            return false;
        }

    private:
        vector<Node*> _tables; // 指针数组,存储哈希桶
        size_t _n = 0;         // 表中存储数据个数
    };
}

UnorderedMap.h

#pragma once
#include"HashTable.h"

namespace sty
{
    // unordered_map 模板类
    template<class K, class V, class Hash = HashFunc<K>>
    class unordered_map
    {
        // 从pair中提取key的仿函数
        struct MapKeyOfT
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;
            }
        };
    public:
        // 迭代器类型定义
        typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
        typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Const_Iterator const_iterator;

        // 获取起始迭代器
        iterator begin()
        {
            return _ht.Begin();
        }

        // 获取结束迭代器
        iterator end()
        {
            return _ht.End();
        }

        // 获取常量起始迭代器
        const_iterator begin() const
        {
            return _ht.Begin();
        }

        // 获取常量结束迭代器
        const_iterator end() const
        {
            return _ht.End();
        }

        // 下标操作符
        V& operator[](const K& key)
        {
            pair<iterator, bool> ret = insert({ key, V() });
            return ret.first->second;
        }

        // 插入元素
        pair<iterator, bool> insert(const pair<K,V>& kv)
        {
            return _ht.Insert(kv);
        }

        // 查找元素
        iterator Find(const K& key)
        {
            return _ht.Find(key);
        }

        // 删除元素
        bool Erase(const K& key)
        {
            return _ht.Erase(key);
        }

    private:
        // 底层哈希表
        hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
    };


}

UnorderedSet.h

#pragma once
#include"HashTable.h"

namespace sty
{
    // unordered_set 模板类
    template<class K, class Hash = HashFunc<K>>
    class unordered_set
    {
        // 从key中提取key的仿函数(直接返回key本身)
        struct SetKeyOfT
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };
    public:
        // 迭代器类型定义
        typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
        typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Const_Iterator const_iterator;

        // 获取起始迭代器
        iterator begin()
        {
            return _ht.Begin();
        }

        // 获取结束迭代器
        iterator end()
        {
            return _ht.End();
        }

        // 获取常量起始迭代器
        const_iterator begin() const
        {
            return _ht.Begin();
        }

        // 获取常量结束迭代器
        const_iterator end() const
        {
            return _ht.End();
        }

        // 插入元素
        pair<iterator, bool> insert(const K& key)
        {
            return _ht.Insert(key);
        }

        // 查找元素
        iterator Find(const K& key)
        {
            return _ht.Find(key);
        }

        // 删除元素
        bool Erase(const K& key)
        {
            return _ht.Erase(key);
        }

    private:
        // 底层哈希表
        hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
    };



}

你可能感兴趣的:(c++,开发语言)