unordered_map以char *作为key值

unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。

不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。

嗯,总的来说,按照查找效率排名:unordered_map > hash_map > map;

我最近一个项目中,需要利用unordered_map进行字符串和int类型的映射,本来一开始使用unordered_map<string, int>即string作为key的键值的。但是在查找过程中,当传入一个字符串时,在底层它会复制一份到临时的string类型后再进行查找,这就产生了性能损失,由于项目需要尽可能好的性能,所以最终决定使用char *类型作为键值。

unordered_map的原型如下:
template<class Key,
    class Ty,
    class Hash = std::hash<Key>,
    class Pred = std::equal_to<Key>,
    class Alloc = std::allocator<std::pair<const Key, Ty> > >
    class unordered_map;>
其中Key是键类型。Ty是映射类型,也就是值。Hash是哈希函数对象类型,表示存储的过程中所使用的哈希函数,详细的情况可网上搜索哈希表结构。
Pred是相等比较函数对象类型,当查找时,如果传进来的字符串(假设这里是通过字符串作为key的)哈希后的哈希值与某个key的哈希值相同,将会进一步通过Pred函数进行比较判断,如果判断为真,则代表查找到了该值了。

使用const char*字符串作为键值的话,需要做两件事,一是重载比较函数Pred,二是重载Hash函数。
重载比较函数的原因是,如果使用它本身的比较函数std::equal_to<const char*>的话,它比较的仅仅只是两个指针的地址是否相同,而不是两个指针指向的字符串是否相同,因此需要重载使用strcmp进行判断,如下面的例子struct cmp。
重载哈希函数的原因也类似,因为如果使用hash<const char*>的话,存储时确实能正确地构造出一个哈希列表,但是它是用指针地址作为key值进行哈希的,而不是指针指向的字符串进行哈希,所以在传入一个字符串查找(比如使用iter = find(str)或者unorder_map[str])时,永远也查不到你需要的值。(本人在这里卡了很久,深深的噩梦啊)详细的可以看http://en.cppreference.com/w/cpp/utility/hash
链接里面有一句话十分关键!!十分关键!!就是下面代码里面注释的那句话!!

以下是我的代码:
struct cmp
{
bool operator()(const char *s1, const char *s2) const
{
return std::strcmp(s1, s2) == 0;
}

};

struct hash_func
{
size_t operator()(const char *str) const
{
int strLength = 0;

for (; *(str + strLength) != '\0'; ++strLength);

return std::_Hash_seq((unsigned char *)str, strLength );
}
};
/*There is no specialization for C strings.std::hash<const char*>
produces a hash of the value of the pointer(the memory address),
it does not examine the contents of any character array.*/
typedef std::unordered_map<const char*, int, hash_func, cmp> XMyMap;

XMyTable Sheet;

上面的hash_func是我自己跟踪进std::hash<string>里面,发现它是调用_Hash_seq的,于是在这里我依照它的情况使用了该函数,而不是按网上普遍的使用自己实现的哈希函数。_Hash_seq_()第一个参数是(unsigned char*)字符串指针,第二个参数是字符串的长度。

希望这篇文章能给后来有强迫症和性能癖的小伙伴们一点帮助^_^
<think>我们被要求遍历unordered_map<char, int>的键(key。根据引用[1]和引用[2]中的示例,我们可以使用迭代器或基于范围的for循环来遍历unordered_map的键。注意:unordered_map的每个元素是一个pair,其中first是键,second是。方法1:使用迭代器(正向迭代器,因为unordered_map不支持反向迭代器)unordered_map<char,int>::iterator it= map.begin();while(it!= map.end()){charkey =it->first;//获取键//使用key做操作++it;}方法2:使用基于范围的for循环(C++11及以上)for (auto&kv :map){char key= kv.first;//获取键//使用key做操作}方法3:如果只需要键而不需要,我们也可以使用结构化绑定(C++17及以上):for (auto&[key, value] :map){//直接使用key,如果不需要value,可以忽略}但是,如果我们只关心键,也可以使用C++17的结构化绑定只取key(虽然这样会忽略value,但语法上需要写两个变量,不过我们可以用std::ignore?实际上不需要,因为我们不访问value,所以直接写两个变量名,但只使用key即可。或者我们也可以只取first,即用方法2)。另外,C++11及以上,我们还可以使用auto和pair来遍历:for (auto kv: map) {charkey =kv.first;}但是,如果我们只需要键,而不需要,还有一种方法:遍历map的键的集合。我们可以使用C++11的for循环结合auto,然后只取键。或者我们可以使用迭代器,然后通过迭代器访问first。另外,我们也可以使用C++20的ranges,但这里不涉及,因为题目没有要求。根据引用[1]中的示例,他们使用了迭代器遍历,并输出键值对。引用[2]中使用了基于范围的for循环,并使用了auto和pair(或者auto kv)来遍历。因此,我们提供以下两种常用方法:示例代码:假设我们有一个unordered_map<char,int>,名为map,我们要遍历它的所有键并打印。方法一:迭代器for(unordered_map<char, int>::iteratorit =map.begin(); it!= map.end();++it) {cout<< it->first<< endl;}方法二:基于范围的for循环(推荐)for(auto& kv: map) {cout<< kv.first <<endl;}方法三:只取键(使用结构化绑定,C++17)for (auto&[key, value] :map){cout <<key <<endl;}注意:如果不需要修改键,我们可以使用const auto&或者直接auto(复制)。但键是char,复制开销很小,所以无所谓。另外,如果我们想避免复制键(如果是大对象,但这里键是char,所以复制很便宜),但习惯上,我们可以使用const auto&来避免复制,并保护原数据不被修改。但是,由于我们只取键,而且键是char,所以我们可以这样写:for(constauto&kv :map){char key= kv.first;//或者直接使用kv.first}或者,如果我们只想要键,而不关心,我们也可以使用C++11的for循环,然后只取first。另外,我们还可以使用std::for_each算法,但这里不展开。现在,我们写一个完整的示例: ```cpp#include <iostream>#include <unordered_map> usingnamespace std; intmain(){unordered_map<char,int>map ={{'a',1},{'b',2},{'c',3}};//方法1:迭代器cout <<"方法1(迭代器):"<< endl;for(unordered_map<char, int>::iteratorit =map.begin(); it!= map.end();++it) {cout<< it->first<< endl;}//方法2:基于范围的for循环(使用pair)cout<< "方法2(基于范围的for循环):" <<endl;for(auto& kv: map) {cout<< kv.first <<endl;}//方法3:结构化绑定(C++17)cout <<"方法3(结构化绑定):"<< endl;for (auto&[key, value] :map){cout <<key <<endl;}return0; }```输出:abc(注意:unordered_map是无序的,所以输出顺序不一定是插入顺序)注意:在方法3中,我们使用了C++17的结构化绑定,所以需要编译器支持C++17。总结:推荐使用方法2或方法3,简洁易读。另外,如果我们只需要键,而不需要,还有一种方法:我们可以先获取所有的键(放到一个vector中),然后遍历vector。但是这样会额外消耗空间,除非我们需要多次遍历键,否则直接遍历map即可。例如:vector<char>keys;for(auto& kv: map) {keys.push_back(kv.first);}//然后遍历keys但这种方法不推荐,除非有特殊需求(比如需要对键进行排序等)。因此,直接遍历map获取键是最直接的方法。请根据实际需求选择合适的方法。 §§相关问题§§1.如何遍历unordered_map(value)? 2.在C++中,unordered_mapmap在遍历时有什么不同? 3. C++17结构化绑定还有哪些应用场景?</think>### 如何遍历C++中`unordered_map<char, int>`的键(key) 在C++中遍历`unordered_map<char, int>`的键(key)有多种高效方法,以下是三种常用方式: #### 方法1:使用迭代器(最基础) ```cpp unordered_map<char, int> myMap = {{'a', 1}, {'b', 2}, {'c', 3}}; for (auto it = myMap.begin(); it != myMap.end(); ++it) { char key = it->first; // 获取键 cout << key << " "; // 输出: a b c (顺序不确定) } ``` **特点**: - 使用标准迭代器访问每个元素 - `it->first`获取键,`it->second`获取 - 适用于所有C++版本[^1] #### 方法2:基于范围的for循环(C++11推荐) ```cpp for (auto& kv : myMap) { // kv是键值对的引用 char key = kv.first; cout << key << " "; // 输出: a b c (顺序不确定) } ``` **优势**: - 语法简洁,减少迭代器操作 - `auto`自动推导类型 - 避免不必要的拷贝(使用引用`&`)[^2] #### 方法3:结构化绑定(C++17最简洁) ```cpp for (auto& [key, value] : myMap) { // 直接解构键和 cout << key << " "; // 输出: a b c (顺序不确定) } ``` **优势**: - 直接访问键值,无需`.first/.second` - 代码可读性最高 - 支持同时访问键值(`value`变量包含) #### 注意事项: 1. **无序性**:`unordered_map`的遍历顺序与插入顺序无关,由哈希函数决定[^1] 2. **性能**:遍历时间复杂度为$O(n)$,平均$O(1)$访问每个元素 3. **键不可修改**:遍历中不能修改键(会破坏哈希结构),可通过`kv.second`修改 4. **map处理**:开始遍历前建议检查`!myMap.empty()` #### 完整示例: ```cpp #include <iostream> #include <unordered_map> using namespace std; int main() { unordered_map<char, int> charMap = {{'X', 10}, {'Y', 20}, {'Z', 30}}; cout << "Keys: "; for (auto& [key, val] : charMap) { cout << key << " "; // 输出: X Y Z (顺序可能变化) } return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值