用struct做unordered_map的key
本篇文章著重討論如何在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> 函式,以便於根據Key型別的例項來獲得在雜湊表中的位置。(作為第4個引數)
2. 定義 operator == 以便於在 Hash 衝突時比較真正的key值是否相等。(operator == 被第3個引數預設的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;
}
(完)
相關文章
- struct的tag到底可以用來做什麼?Struct
- 何時用 struct?何時用 class?Struct
- Cpp學習 -- <unordered_map>
- struct的一種用法Struct
- struct和typedef struct 有什麼不同呢?Struct
- struct的匿名用法詳解Struct
- 用實數作為 HashMap 的key,被坑哭了HashMap
- Redis 實用小技巧——批次刪除指定的 keyRedis
- Ruby Struct EqualStruct
- c++ map和unordered_map比較C++
- unordered_map隨機底數種子隨機
- C時間函式strftime、struct timespec 和 struct timeval函式Struct
- map、unordered_map、set 和 unordered_set的小介紹
- struct轉map (反射)Struct反射
- swift中Class和Struct的區別SwiftStruct
- mysql中key 、primary key 、unique key 與index區別MySqlIndex
- Redis熱點key大keyRedis
- 結構struct(值型別)在實際應用中應該注意的點Struct型別
- 記一個關於std::unordered_map併發訪問的BUG
- 用 Roslyn 做個 JIT 的 AOPROS
- array+map+struct.hqlStruct
- typedef and struct inside class definition?StructIDE
- Go語言之 Struct TagGoStruct
- 重學c#————structC#Struct
- 當 Go struct 遇上 MutexGoStructMutex
- Flutter中的Key(一)Flutter
- Flutter中的Key(二)Flutter
- Vue中key的作用Vue
- 關於c++ STL map 和 unordered_map 的效率的對比測試C++
- 請教一個struct tag的問題Struct
- C++中struct的空間計算C++Struct
- 深入理解Swift中的Class和StructSwiftStruct
- GO 同 (異) 包呼叫以及 struct 的用法GoStruct
- 文盤Rust -- struct 中的生命週期RustStruct
- COCOs2dx中KEY_KP與KEY的區別
- 不能使用列舉類作為unordered_map鍵
- 用 canvas 的 getImageData 做點有趣的事Canvas
- tslib 這個包做啥用的