c++11 std::hash 的使用

本文介绍了C++标准库中的哈希模板结构std::hash,包括其成员类型、成员函数及针对不同内置类型和标准库类型的特化实现,并提供了计算字符串和自定义类型的哈希值的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文转自:http://zh.cppreference.com/w/cpp


在头文件 <functional> 中定义
  
   
template<class Key >
struct hash;// not defined
(C++11 起)
   

哈希模板定义一个函数对象,实现了散列函数。这个函数对象的实例定义一个operator()

1。接受一个参数的类型Key.

2。返回一个类型为size_t的值,表示该参数的哈希值.

3。调用时不会抛出异常.

4。若两个参数k1k2相等,则std::hash<Key>()(k1)== std::hash<Key>()(k2).

5。若两个不同的参数k1k2不相等,则std::hash<Key>()(k1)== std::hash<Key>()(k2)成立的概率应非常小,接近1.0/std::numeric_limits<size_t>::max().

哈希模板是CopyConstructibleDestructible.

的无序关联式容器std::unordered_setstd::unordered_multisetstd::unordered_mapstd::unordered_multimap使用专业的模板std::hash默认的散列函数.

目录

 [隐藏

[编辑]会员类型

 
argument_typeKey
 
result_typestd::size_t

[编辑]成员函数

 
构造一个哈希函数对象
原文:
constructs a hash function object
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(公共成员函数)
 
计算哈希的说法
原文:
calculate the hash of the argument
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(公共成员函数)

[编辑]标准的专业基本类型

在头文件 <functional> 中定义
  
   
template<>struct hash<bool>;

template<>struct hash<char>;
template<>struct hash<signedchar>;
template<>struct hash<unsignedchar>;
template<>struct hash<char16_t>;
template<>struct hash<char32_t>;
template<>struct hash<wchar_t>;
template<>struct hash<short>;
template<>struct hash<unsignedshort>;
template<>struct hash<int>;
template<>struct hash<unsignedint>;
template<>struct hash<long>;
template<>struct hash<longlong>;
template<>struct hash<unsignedlong>;
template<>struct hash<unsignedlong long>;
template<>struct hash<float>;
template<>struct hash<double>;
template<>struct hash<longdouble>;

template < class T > struct hash <T * > ;

[编辑]标准库类型专业

 
字符串哈希支持
原文:
hash support for strings
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::error_code的哈希支持
原文:
hash support for std::error_code
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::bitset的哈希支持
原文:
hash support for std::bitset
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::unique_ptr的哈希支持
原文:
hash support for std::unique_ptr
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::shared_ptr的哈希支持
原文:
hash support for std::shared_ptr
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::type_index的哈希支持
原文:
hash support for std::type_index
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化) [edit]
 
std::vector<bool>的哈希支持
原文:
hash support for std::vector<bool>
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化)
 
std::thread::id的哈希支持
原文:
hash support for std::thread::id
这段文字是通过 Google Translate 自动翻译生成的。
您可以帮助我们检查、纠正翻译中的错误。详情请点击 这里

(类模板特化)

[编辑]的例子

演示std::string,已经有一个哈希专业化的类型的哈希计算.

原文:

Demonstrates the computation of a hash for std::string, a type that already has a hash specialization.

#include <iostream>
#include <functional>
#include <string>
 
int main()
{
    std::string str = "Meet the new boss...";
    std::hash<std::string> hash_fn;
    size_t str_hash = hash_fn(str);
 
    std::cout << str_hash << '\n';
}

输出:

391070135

演示如何创建一个用户定义的类型的哈希函数。以此为模板参数std::unordered_mapstd::unordered_set等也需要专业化的std::equal_to.

原文:

Demonstrates creation of a hash function for a user defined type. Using this as a template parameter forstd::unordered_map, std::unordered_set, etc. also requires specialization ofstd::equal_to.

#include <iostream>
#include <functional>
#include <string>
 
struct S {
    std::string first_name;
    std::string last_name;
};
 
template<class T> class MyHash;
 
template<>
class MyHash<S> {
public:
    size_t operator()(const S &s) const
    {
        size_t h1 = std::hash<std::string>()(s.first_name);
        size_t h2 = std::hash<std::string>()(s.last_name);
        return h1 ^ (h2 << 1);
    }
};
 
int main()
{
    std::string s1 = "Hubert";
    std::string s2 = "Farnsworth";
    std::hash<std::string> h1;
 
    S n1;
    n1.first_name = s1;
    n1.last_name =  s2;
 
    std::cout << "hash(s1) = " << h1(s1) << "\n"
              << "hash(s2) = " << std::hash<std::string>()(s2) << "\n"
          << "hash(n1) = " << MyHash<S>()(n1) << "\n";
 
}

输出:

hash(s1) = 6119893563398376542
hash(s2) = 14988020022735710972
hash(n1) = 17649170831080298918

 

 

 

演示如何专门std::hash为用户定义的类型.

原文:

Demonstrates how to specialize std::hash for a user defined type.

#include <iostream>
#include <functional>
#include <string>
 
struct S {
    std::string first_name;
    std::string last_name;
};
 
namespace std {
template<>
class hash<S> {
public:
    size_t operator()(const S &s) const
    {
        size_t h1 = std::hash<std::string>()(s.first_name);
        size_t h2 = std::hash<std::string>()(s.last_name);
        return h1 ^ ( h2 << 1 );
    }
};
}
 
int main()
{
    S s;
    s.first_name = "Bender";
    s.last_name =  "Rodriguez";
    std::hash<S> hash_fn;
 
    std::cout << "hash(s) = " << hash_fn(s) << "\n";
}

输出:

hash(s) = 32902390710
<think>嗯,用户想让我介绍一下如何用C++std::vector来实现哈希表。首先,我得先回忆一下哈希表的基本原理。哈希表通常是通过数组和链表实现的,也就是拉链法,或者开放地址法。用户提到要用vector,那应该就是用vector来代替数组的部分,可能结合链表来处理冲突。 然后,我需要考虑如何用vector来构建哈希表的结构。每个桶可能是一个vector的元素,而每个元素本身可能是一个链表,比如用std::list来存储键值对。或者,如果用户想要更简单,也许可以用vector的vector,也就是每个桶是一个vector,存储键值对的数组。不过这样在冲突时,查找效率可能不如链表高效,但实现起来可能更简单。 接下来,哈希函数的选择。通常可以用取模运算,比如对vector的大小取模,这样键的哈希值会被映射到不同的桶中。需要注意的是,当哈希表扩容时,vector的大小改变,这时候所有的键都需要重新哈希,重新分配到新的桶里。这可能涉及到负载因子的概念,当元素数量超过负载因子乘以当前容量时,进行扩容。 然后,实现哈希表的基本操作:插入、查找、删除。插入的时候,先计算哈希值找到对应的桶,然后检查是否已经有相同的键存在,如果有的话可能需要更新值,否则添加新的键值对。查找也是类似的流程,计算哈希值,遍历桶中的元素寻找匹配的键。删除操作则需要找到元素后从桶中移除。 还有一个问题是处理冲突。如果用拉链法,每个桶是一个链表的话,插入到链表头部或尾部都可以。如果用开放地址法,可能需要在vector中线性探测或者其他方式,但用vector的话可能不太方便,因为删除操作会比较麻烦。所以可能拉链法更适合用vector来实现,每个桶用std::list或者另一个vector来存储。 另外,需要考虑动态扩容。当元素数量增加导致负载因子超过阈值时,需要创建一个更大的vector,通常是原来的两倍,然后重新哈希所有现有的元素到新的vector中。这时候,哈希函数会基于新的vector大小来计算新的位置。这个过程可能会影响性能,但能保持较低的冲突率。 可能还需要考虑迭代器的实现,但用户可能只是想要一个基本的实现,所以这部分可以暂时忽略。另外,键的类型需要是可哈希的,或者用户提供哈希函数。在C++中,可以使用std::hash模板来生成默认的哈希值,但用户可能需要自定义类型时特化这个模板。 测试的时候,需要注意各种边界情况,比如插入重复键、删除不存在的键、哈希表扩容后的正确性等。还有性能方面的考量,比如负载因子设置是否合理,哈希函数是否分布均匀等。 总结一下,实现步骤大致是:定义一个模板类,包含vector作为底层存储,每个桶是一个链表(比如std::list),哈希函数计算键的位置,处理冲突,实现插入、查找、删除方法,以及动态扩容的逻辑。需要注意线程安全的话可能还要加锁,但用户没有提到,所以可能不需要考虑。 现在,我需要把这些思考整理成一个清晰的步骤,用中文解释给用户,可能需要代码示例来说明关键部分,比如哈希表的结构、插入和扩容的过程。同时,要指出这种实现的优缺点,比如相比标准库的std::unordered_map,可能在性能上有所不同,或者功能上的局限性。</think>以下是用C++标准库的std::vector实现哈希表的完整指南,采用中文分步骤说明: --- ### **实现原理** 哈希表通过哈希函数将键映射到存储桶(bucket),采用vector作为底层容器实现动态数组特性,使用拉链法(链表)处理哈希冲突。当负载因子(元素数量/桶数量)超过阈值时自动扩容。 --- ### **核心实现步骤** #### **1. 定义哈希表结构** ```cpp template <typename K, typename V> class HashMap { private: struct Node { K key; V value; Node(K k, V v) : key(k), value(v) {} }; std::vector<std::list<Node>> buckets; // 桶数组 size_t numElements = 0; // 元素总数 const double LOAD_FACTOR = 0.75; // 扩容阈值 // 计算桶索引 size_t getBucketIndex(const K& key) const { return std::hash<K>{}(key) % buckets.size(); } }; ``` #### **2. 构造函数初始化** ```cpp HashMap() { buckets.resize(10); // 初始10个空桶 } ``` #### **3. 插入键值对(自动扩容)** ```cpp void insert(const K& key, const V& value) { // 检查是否需要扩容 if ((double)numElements / buckets.size() > LOAD_FACTOR) { rehash(); } size_t index = getBucketIndex(key); auto& chain = buckets[index]; // 检查键是否已存在 for (auto& node : chain) { if (node.key == key) { node.value = value; // 更新值 return; } } // 插入新节点 chain.emplace_back(key, value); numElements++; } ``` #### **4. 动态扩容(Rehash)** ```cpp void rehash() { std::vector<std::list<Node>> newBuckets(buckets.size() * 2); for (auto& chain : buckets) { for (auto& node : chain) { size_t newIndex = std::hash<K>{}(node.key) % newBuckets.size(); newBuckets[newIndex].push_back(node); } } buckets.swap(newBuckets); // 原子操作替换桶数组 } ``` #### **5. 查找操作** ```cpp V* find(const K& key) { size_t index = getBucketIndex(key); auto& chain = buckets[index]; for (auto& node : chain) { if (node.key == key) { return &node.value; } } return nullptr; // 未找到 } ``` #### **6. 删除操作** ```cpp bool erase(const K& key) { size_t index = getBucketIndex(key); auto& chain = buckets[index]; for (auto it = chain.begin(); it != chain.end(); ++it) { if (it->key == key) { chain.erase(it); numElements--; return true; } } return false; } ``` --- ### **关键特性** 1. **哈希函数**:使用`std::hash`生成默认哈希值 2. **冲突处理**:链表法(每个桶是`std::list`) 3. **动态扩容**:当负载因子>0.75时容量翻倍 4. **时间复杂度**: - 平均情况:$O(1)$ - 最坏情况:$O(n)$(所有键哈希冲突时) --- ### **与std::unordered_map对比** | 特性 | 本实现 | std::unordered_map | |---------------------|------------------|-------------------------| | 底层结构 | vector + list | 动态数组 + 链表 | | 内存管理 | 手动控制 | 自动优化 | | 迭代器支持 | 无 | 完整迭代器 | | 最大负载因子 | 固定0.75 | 可自定义 | | 异常安全 | 基础保证 | 强异常安全 | --- ### **优化方向建议** 1. **定制哈希函数**:为自定义类型特化`std::hash` 2. **开放寻址法**:改用线性探测/二次探测 3. **内存预分配**:根据预估数据量初始化容量 4. **移动语义**:添加右值引用支持 5. **迭代器实现**:支持STL风格遍历 这种实现方式虽然比标准库的`unordered_map`简单,但完整展示了哈希表的核心机制,适合教学理解或特定场景优化。实际生产环境建议优先使用标准库实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值