ITPub博客

首页 > 应用开发 > IT综合 > 哈希表容器封装:unordered_map与unordered_set的实现解析

哈希表容器封装:unordered_map与unordered_set的实现解析

投稿 IT综合 作者:bhluo 时间:2025-12-10 14:10:15 0 删除 编辑

## 哈希表容器封装:unordered_map与unordered_set的实现解析


在C++标准库中,unordered_map和unordered_set作为基于哈希表的关联容器,提供了高效的查找和插入操作。本文将深入探讨如何通过哈希表封装实现这两个核心容器,分析其内部机制和设计模式。


### 基础架构设计:模板化哈希表实现


哈希表封装的核心在于设计一个通用的哈希表基础结构,支持不同类型的键值对存储。我们首先构建一个可复用的哈希表模板。


```cpp

#include <vector>

#include <list>

#include <functional>

#include <memory>


// 前向声明哈希表类

template<typename T>

class HashTable;


// 哈希节点基类模板

template<typename T>

struct HashNode {

    T data;

    HashNode* next;

    

    HashNode(const T& val) : data(val), next(nullptr) {}

    virtual ~HashNode() = default;

};


// 特化版本:用于unordered_map的键值对节点

template<typename Key, typename Value>

struct HashNode<std::pair<Key, Value>> {

    std::pair<Key, Value> data;

    HashNode* next;

    

    HashNode(const std::pair<Key, Value>& val) : data(val), next(nullptr) {}

    HashNode(Key k, Value v) : data(std::make_pair(k, v)), next(nullptr) {}

};

```


### 通用哈希表模板实现


```cpp

template<typename T, typename Hash = std::hash<T>, typename KeyEqual = std::equal_to<T>>

class HashTable {

protected:

    using NodeType = HashNode<T>;

    

    std::vector<NodeType*> buckets;

    size_t bucket_count;

    size_t element_count;

    float max_load_factor;

    

    Hash hasher;

    KeyEqual key_equal;

    

    // 获取桶索引

    size_t get_bucket_index(const T& value) const {

        return hasher(get_key(value)) % bucket_count;

    }

    

    // 提取键的通用方法(通过特化实现)

    virtual const auto& get_key(const T& value) const {

        return value;  // 默认情况下,值本身就是键

    }

    

public:

    HashTable(size_t initial_buckets = 16, 

              float load_factor = 0.75f)

        : bucket_count(initial_buckets), 

          element_count(0),

          max_load_factor(load_factor) {

        buckets.resize(bucket_count, nullptr);

    }

    

    virtual ~HashTable() {

        clear();

    }

    

    // 插入元素

    bool insert(const T& value) {

        check_load_factor();

        

        size_t index = get_bucket_index(value);

        NodeType* current = buckets[index];

        

        // 检查是否已存在

        while (current) {

            if (key_equal(get_key(current->data), get_key(value))) {

                return false;  // 已存在

            }

            current = current->next;

        }

        

        // 创建新节点并插入到链表头部

        NodeType* newNode = create_node(value);

        newNode->next = buckets[index];

        buckets[index] = newNode;

        element_count++;

        

        return true;

    }

    

    // 查找元素

    NodeType* find(const T& value) const {

        size_t index = get_bucket_index(value);

        NodeType* current = buckets[index];

        

        while (current) {

            if (key_equal(get_key(current->data), get_key(value))) {

                return current;

            }

            current = current->next;

        }

        

        return nullptr;

    }

    

    // 删除元素

    bool erase(const T& value) {

        size_t index = get_bucket_index(value);

        NodeType* current = buckets[index];

        NodeType* prev = nullptr;

        

        while (current) {

            if (key_equal(get_key(current->data), get_key(value))) {

                if (prev) {

                    prev->next = current->next;

                } else {

                    buckets[index] = current->next;

                }

                

                delete_node(current);

                element_count--;

                return true;

            }

            prev = current;

            current = current->next;

        }

        

        return false;

    }

    

    // 清空哈希表

    void clear() {

        for (size_t i = 0; i < bucket_count; ++i) {

            NodeType* current = buckets[i];

            while (current) {

                NodeType* next = current->next;

                delete_node(current);

                current = next;

            }

            buckets[i] = nullptr;

        }

        element_count = 0;

    }

    

    size_t size() const { return element_count; }

    bool empty() const { return element_count == 0; }

    float load_factor() const { 

        return static_cast<float>(element_count) / bucket_count; 

    }

    

protected:

    virtual NodeType* create_node(const T& value) {

        return new NodeType(value);

    }

    

    virtual void delete_node(NodeType* node) {

        delete node;

    }

    

private:

    void check_load_factor() {

        if (load_factor() >= max_load_factor) {

            rehash(bucket_count * 2);

        }

    }

    

    void rehash(size_t new_bucket_count) {

        std::vector<NodeType*> new_buckets(new_bucket_count, nullptr);

        

        for (size_t i = 0; i < bucket_count; ++i) {

            NodeType* current = buckets[i];

            while (current) {

                NodeType* next = current->next;

                

                size_t new_index = hasher(get_key(current->data)) % new_bucket_count;

                current->next = new_buckets[new_index];

                new_buckets[new_index] = current;

                

                current = next;

            }

            buckets[i] = nullptr;

        }

        <"5j.acbf.cn"><"y9.adgp.cn"><"e2.adgp.cn">

        buckets = std::move(new_buckets);

        bucket_count = new_bucket_count;

    }

};

```


### unordered_set的实现


unordered_set是存储唯一元素的容器,基于哈希表实现集合操作。


```cpp

template<typename Key, 

         typename Hash = std::hash<Key>, 

         typename KeyEqual = std::equal_to<Key>>

class UnorderedSet : public HashTable<Key, Hash, KeyEqual> {

public:

    using Base = HashTable<Key, Hash, KeyEqual>;

    using iterator = typename Base::iterator;

    using const_iterator = typename Base::const_iterator;

    

    UnorderedSet(size_t initial_buckets = 16, float load_factor = 0.75f)

        : Base(initial_buckets, load_factor) {}

    

    // 集合特有操作

    bool contains(const Key& key) const {

        return this->find(key) != nullptr;

    }

    

    // 插入键(重写基类方法)

    std::pair<iterator, bool> insert(const Key& key) {

        bool inserted = Base::insert(key);

        // 这里需要返回迭代器,简化实现

        return std::make_pair(iterator(), inserted);

    }

    

    // 擦除键

    size_t erase(const Key& key) {

        return Base::erase(key) ? 1 : 0;

    }

    

    // 迭代器支持

    class iterator {

    private:

        using NodeType = typename Base::NodeType;

        NodeType* current;

        const UnorderedSet* container;

        size_t bucket_index;

        

        void advance_to_next() {

            if (!current) return;

            

            // 先尝试移动到链表下一个节点

            if (current->next) {

                current = current->next;

                return;

            }

            

            // 当前链表已结束,查找下一个非空桶

            for (size_t i = bucket_index + 1; i < container->bucket_count; ++i) {

                if (container->buckets[i]) {

                    current = container->buckets[i];

                    bucket_index = i;

                    return;

                }

            }

            

            // 没有更多元素

            current = nullptr;

        }

        

    public:

        iterator(NodeType* node = nullptr, 

                 const UnorderedSet* cont = nullptr, 

                 size_t index = 0)

            : current(node), container(cont), bucket_index(index) {}

        

        Key& operator*() const {

            return current->data;

        }

        

        Key* operator->() const {

            return &(current->data);

        }

        

        iterator& operator++() {

            advance_to_next();

            return *this;

        }

        

        bool operator==(const iterator& other) const {

            return current == other.current;

        }

        

        bool operator!=(const iterator& other) const {

            return !(*this == other);

        }

    };

    

    iterator begin() {

        for (size_t i = 0; i < this->bucket_count; ++i) {

            if (this->buckets[i]) {

                return iterator(this->buckets[i], this, i);

            }

        }

        return end();

    }

    

    iterator end() {

        return iterator(nullptr, this, this->bucket_count);

    }

};

```


### unordered_map的实现


unordered_map存储键值对,需要扩展哈希表以支持键值分离。


```cpp

template<typename Key, typename Value,

         typename Hash = std::hash<Key>,

         typename KeyEqual = std::equal_to<Key>>

class UnorderedMap : public HashTable<std::pair<Key, Value>, Hash, KeyEqual> {

private:

    using Base = HashTable<std::pair<Key, Value>, Hash, KeyEqual>;

    using NodeType = typename Base::NodeType;

    

    // 重写基类的get_key方法

    const Key& get_key(const std::pair<Key, Value>& value) const override {

        return value.first;

    }

    

protected:

    NodeType* create_node(const std::pair<Key, Value>& value) override {

        return new HashNode<std::pair<Key, Value>>(value);

    }

    

public:

    UnorderedMap(size_t initial_buckets = 16, float load_factor = 0.75f)

        : Base(initial_buckets, load_factor) {}

    

    // 插入或更新键值对

    std::pair<iterator, bool> insert(const std::pair<Key, Value>& kv) {

        return insert_impl(kv.first, kv.second);

    }

    

    // 使用键值对插入

    std::pair<iterator, bool> insert(const Key& key, const Value& value) {

        return insert_impl(key, value);

    }

    

    // 下标操作符

    Value& operator[](const Key& key) {

        NodeType* node = this->find(std::make_pair(key, Value()));

        if (!node) {

            auto result = insert_impl(key, Value());

            node = result.first.current;  // 简化处理

        }

        return node->data.second;

    }

    

    // at方法(带边界检查)

    Value& at(const Key& key) {

        NodeType* node = this->find(std::make_pair(key, Value()));

        if (!node) {

            throw std::out_of_range("Key not found in unordered_map");

        }

        return node->data.second;

    }

    

    const Value& at(const Key& key) const {

        NodeType* node = this->find(std::make_pair(key, Value()));

        if (!node) {

            throw std::out_of_range("Key not found in unordered_map");

        }

        return node->data.second;

    }

    

    // 查找键

    iterator find(const Key& key) {

        NodeType* node = this->find(std::make_pair(key, Value()));

        if (node) {

            // 需要找到对应的桶索引

            size_t index = this->get_bucket_index(std::make_pair(key, Value()));

            return iterator(node, this, index);

        }

        return end();

    }

    

    // 擦除键

    size_t erase(const Key& key) {

        return this->erase(std::make_pair(key, Value())) ? 1 : 0;

    }

    

private:

    std::pair<iterator, bool> insert_impl(const Key& key, const Value& value) {

        this->check_load_factor();

        

        size_t index = this->get_bucket_index(std::make_pair(key, value));

        NodeType* current = this->buckets[index];

        

        // 检查是否已存在相同键

        while (current) {

            if (this->key_equal(get_key(current->data), key)) {

                // 已存在,更新值

                current->data.second = value;

                return std::make_pair(iterator(current, this, index), false);

            }

            current = current->next;

        }

        <"u5.adgp.cn"><"b1.adgp.cn"><"x8.adgp.cn">

        // 创建新节点

        NodeType* newNode = create_node(std::make_pair(key, value));

        newNode->next = this->buckets[index];

        this->buckets[index] = newNode;

        this->element_count++;

        

        return std::make_pair(iterator(newNode, this, index), true);

    }

    

public:

    // 迭代器类

    class iterator {

    private:

        NodeType* current;

        const UnorderedMap* container;

        size_t bucket_index;

        

        void advance_to_next() {

            if (!current) return;

            

            if (current->next) {

                current = current->next;

                return;

            }

            

            for (size_t i = bucket_index + 1; i < container->bucket_count; ++i) {

                if (container->buckets[i]) {

                    current = container->buckets[i];

                    bucket_index = i;

                    return;

                }

            }

            

            current = nullptr;

        }

        

    public:

        iterator(NodeType* node = nullptr, 

                 const UnorderedMap* cont = nullptr, 

                 size_t index = 0)

            : current(node), container(cont), bucket_index(index) {}

        

        std::pair<Key, Value>& operator*() const {

            return current->data;

        }

        

        std::pair<Key, Value>* operator->() const {

            return &(current->data);

        }

        

        iterator& operator++() {

            advance_to_next();

            return *this;

        }

        

        bool operator==(const iterator& other) const {

            return current == other.current;

        }

        

        bool operator!=(const iterator& other) const {

            return !(*this == other);

        }

    };

    

    iterator begin() {

        for (size_t i = 0; i < this->bucket_count; ++i) {

            if (this->buckets[i]) {

                return iterator(this->buckets[i], this, i);

            }

        }

        return end();

    }

    

    iterator end() {

        return iterator(nullptr, this, this->bucket_count);

    }

};

```


### 优化与扩展功能


**自定义内存分配器支持**


```cpp

template<typename T, typename Allocator = std::allocator<T>>

class CustomAllocatorHashTable : public HashTable<T> {

private:

    using Base = HashTable<T>;

    using NodeAllocator = typename Allocator::template rebind<HashNode<T>>::other;

    

    NodeAllocator node_allocator;

    

protected:

    typename Base::NodeType* create_node(const T& value) override {

        typename Base::NodeType* node = node_allocator.allocate(1);

        node_allocator.construct(node, value);

        return node;

    }

    

    void delete_node(typename Base::NodeType* node) override {

        node_allocator.destroy(node);

        node_allocator.deallocate(node, 1);

    }

};

```


**并发访问支持**


```cpp

#include <shared_mutex>


template<typename T>

class ThreadSafeHashTable : public HashTable<T> {

private:

    using Base = HashTable<T>;

    

    mutable std::vector<std::shared_mutex> bucket_mutexes;

    

public:

    ThreadSafeHashTable(size_t initial_buckets = 16, float load_factor = 0.75f)

        : Base(initial_buckets, load_factor) {

        bucket_mutexes.resize(initial_buckets);

    }

    

    bool thread_safe_insert(const T& value) {

        size_t index = this->get_bucket_index(value);

        

        {

            std::unique_lock lock(bucket_mutexes[index]);

            return Base::insert(value);

        }

    }

    

    typename Base::NodeType* thread_safe_find(const T& value) const {

        size_t index = this->get_bucket_index(value);

        

        {

            std::shared_lock lock(bucket_mutexes[index]);

            return Base::find(value);

        }

    }

};

```


### 使用示例与测试


```cpp

void test_containers() {

    // 测试UnorderedSet

    UnorderedSet<int> intSet;

    intSet.insert(1);

    intSet.insert(2);

    intSet.insert(3);

    

    std::cout << "Set contains 2: " << intSet.contains(2) << std::endl;

    std::cout << "Set size: " << intSet.size() << std::endl;

    

    // 测试UnorderedMap

    UnorderedMap<std::string, int> wordCount;

    wordCount["apple"] = 5;

    wordCount["banana"] = 3;

    wordCount["cherry"] = 7;

    

    // 使用下标操作符访问

    wordCount["apple"] += 1;

    

    // 使用at方法访问(带边界检查)

    try {

        int count = wordCount.at("apple");

        std::cout << "Apple count: " << count << std::endl;

    } catch (const std::out_of_range& e) {

        std::cerr << e.what() << std::endl;

    }

    

    // 遍历map

    std::cout << "\nWord counts:" << std::endl;

    for (auto it = wordCount.begin(); it != wordCount.end(); ++it) {

        std::cout << it->first << ": " << it->second << std::endl;

    }

    

    // 使用C++11范围for遍历

    std::cout << "\nUsing range-based for loop:" << std::endl;

    for (const auto& [key, value] : wordCount) {

        std::cout << key << ": " << value << std::endl;

    }

}

```


### 性能分析与优化建议


1. **负载因子调优**:根据实际使用场景调整最大负载因子

2. **初始桶数量**:预先分配合理数量的桶减少rehash次数

3. **哈希函数选择**:为自定义类型提供高质量的哈希函数

4. **内存局部性**:考虑使用开放定址法改善缓存命中率

5. **批量操作**:添加批量插入和删除的优化版本


### 总结


通过哈希表封装实现unordered_map和unordered_set,不仅加深了对这两个重要容器的理解,也展示了如何设计可复用、可扩展的数据结构。关键设计点包括:


1. **模板化设计**:支持任意类型的键和值

2. **继承与特化**:通过继承重用代码,通过特化处理不同类型

3. **迭代器支持**:提供标准的迭代器接口

4. **内存管理**:支持自定义分配器

5. **线程安全**:可选的并发访问支持


这种封装方法展示了如何将底层数据结构转化为符合STL标准的容器,为理解和设计复杂数据结构的封装提供了宝贵参考。在实际应用中,可以根据具体需求进一步优化和扩展这些实现。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3104862/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    934135