用struct做unordered_map的key
以struct作为unordered_map的key本篇文章着重讨论如何在STL的 unordered_map 中以 struct 作为 key.unordered_map 是STL中的关联容器,自然就是一个模板类。其声明如下:template < class Key,// unordered_map::key_typeclass T,..
本篇文章着重讨论如何在STL的 unordered_map 中以 struct 作为 key.
unordered_map 是STL中的关联容器,自然就是一个模板类。其声明如下:
template < class Key, // unordered_map::key_type
class T, // unordered_map::mapped_type
class Hash = hash<Key>, // unordered_map::hasher
class Pred = equal_to<Key>, // unordered_map::key_equal
class Alloc = allocator< pair<const Key,T> > > class unordered_map;
// unordered_map::allocator_type
第1个参数是key的类型,第2个参数是value的类型,第5个是内存分配模型。关键是第3个参数和第4个参数。
第3个参数 Hash 是一个一元的函数对象类型;该函数对象只有一个参数,其类型是哈希表的 Key 的类型。默认情况下,第3个参数传入的是 std::hash<key> ,它返回的是一个类型为 size_t 的数字,用途是在哈希表中定位表项所在。注意,若要自己实现第3个参数的话,首先它是一个类型(如struct),其次要实现这个类型的 operator() 方法。
那么,当哈希表发生冲突(即传入2个不同的key,但 std::hash<key> 返回的是相同的数字)的时候怎么办呢?unordered_map作为STL中哈希表的一种实现,它当然有办法来解决冲突(如开式寻址法或拉链法,这个要看STL中的具体实现);而我们得让 unordered_map 能够知道为什么这2个key不一样。这就是第4个参数的作用了。
第4个参数默认传入的是 std::equal_to<key> 函数对象,它带2个参数,也就是2个key实例,然后返回一个bool变量表示这2个key是否相等。使用者可以自己特化这个 equal_to 函数对象,但因为默认的 std::equal_to<key> 会去调用 key 类型的 operator == 函数,所以使用者其实只要实现 key 类型的 operator == 函数就可以了。
基于以上对于 unordered_map 的模板参数的分析,想把 struct或class 作为 unordered_map 的 key,就需要做以下2件事情。
1. 创建特化的 std::hash<Key> 函数作为第3个参数,以便于根据Key类型的实例来获得在哈希表中的位置。
这一步还有另一个方法,就是自己写一个函数对象,定义 operator () 方法,这样就不用特化的 std::hash<Key> 了
2. 定义 operator == 以便于在 Hash 冲突时比较真正的key值是否相等。(operator == 被第4个参数默认的equal_to使用)
代码实例有2个。第1个请见上篇博客中的代码。第2个代码实例如下:
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
template<typename T1, typename T2>
struct Node
{
T1 x;
T2 y;
Node(T1 a, T2 b) : x(a), y(b) {}
bool operator==(const Node& p) const {
return x == p.x && y == p.y;
}
};
// Way-1: specialized hash function for unordered_map keys
struct hash_fn
{
template <class T1, class T2>
std::size_t operator() (const Node<T1, T2> & node) const {
std::size_t h1 = std::hash<T1>()(node.x);
std::size_t h2 = std::hash<T2>()(node.y);
return h1 ^ h2;
}
};
// Way-2: 特化 std::hash<Node>
namespace std
{
template<typename T1, typename T2>
struct hash<Node<T1, T2>>
{
size_t operator() (const Node<T1, T2> & node) const noexcept
{
std::size_t h1 = std::hash<T1>()(node.x);
std::size_t h2 = std::hash<T2>()(node.y);
return h1 ^ h2;
}
};
}
int main()
{
// Way-1
std::unordered_map< Node<std::string,std::string>, int, hash_fn > u_map1 =
{
{{"C", "C99"}, 1999},
{{"C", "C11"}, 2011},
{{"C++", "C++14"}, 2014},
{{"C++", "C++17"}, 2017},
{{"Java", "Java SE 8"}, 2014},
{{"Java", "Java SE 9"}, 2017}
};
cout << "Way-1: \n";
for (const auto &entry: u_map1)
{
std::cout << "{" << entry.first.x << "," << entry.first.y << "}: "
<< entry.second << '\n';
}
// Way-2
std::unordered_map< Node<std::string,std::string>, int > u_map2 =
{
{{"C", "C99"}, 1999},
{{"C", "C11"}, 2011},
{{"C++", "C++14"}, 2014},
{{"C++", "C++17"}, 2017},
{{"Java", "Java SE 8"}, 2014},
{{"Java", "Java SE 9"}, 2017}
};
cout << "Way-2: \n";
for (const auto &entry: u_map2)
{
std::cout << "{" << entry.first.x << "," << entry.first.y << "}: "
<< entry.second << '\n';
}
return 0;
}
(完)
更多推荐
所有评论(0)