C++ std::unordered_map使用及如何自定義鍵的類型
-
頭文件:<unordered_map>
-
定義:
template<class _Key, class _Tp, class _Hash = hash<_Key>, class _Pred = std::equal_to<_Key>, class _Alloc = std::allocator<std::pair<const _Key, _Tp> > > class unordered_map{...} _Hash = hash<_Key> 由於std::unordered_map 使用哈希算法,將值存儲到桶中,如果自定義鍵的類型,需要實現對應方法。如果使用基本類型則不用提供 _Pred = std::equal_to<_Key> 由於hash算法可能會遇到hash值相同,但內容不同的情況,提供函數來區分 _Alloc = std::allocator<std::pair<const _Key, _Tp> > >則是內存管理使用,一般來說使用默認的內存管理即可
-
公共定義的類型
/// Public typedefs. typedef typename _Hashtable::key_type key_type; //鍵類型 typedef typename _Hashtable::value_type value_type; // std::Pair<const _Key k, _Tp t> typedef typename _Hashtable::mapped_type mapped_type;//值的類型 typedef typename _Hashtable::hasher hasher; typedef typename _Hashtable::key_equal key_equal; typedef typename _Hashtable::allocator_type allocator_type;
-
構造函數
void testCreateMap(){ using Group = std::unordered_map<int,std::string>; Group a; // 默認構造函數 Group b{{1,"12"},{2,"22"}}; // c++11 初始化列表構造 Group c(b.begin(),b.end()); // 兩個迭代器 Group d(std::move(b)); // 移動構造 Group f(d); // 複製構造 std::cout << "d size: " << d.size() << std::endl; for (const auto& item : d){ std::cout << "key: "<< item.first << ",value: " << item.second << std::endl; } } 結果: d size: 2 key: 1,value: 12 key: 2,value: 22
-
插入、查找、遍歷
void test_insert_find_traverse(){ // 使用重載的[]進行插入,查找 // 問題是在查找時,比如例子中13鍵不存在,map會使用string的默認構造函數插入,這時map有兩對pair {12,“sdf"},{13,""} using Group = std::unordered_map<int,std::string>; Group a; a[12] = "use []"; std::cout << a[12] << std::endl; std::cout << a[13] << std::endl; std::cout << a.size() << std::endl; std:: cout << "insert" << std::endl; // 使用insert 或emplace // insert 與 emplace 結果相同,返回的是std::pair<iterator, bool>,iterator是插入位置的迭代器,bool爲是否從插入成功 // iterator 的鍵爲const類型 值可修改 Group b{{12,"init"}}; auto insert_result = b.insert(std::make_pair(12,"insert")); // 查看鍵值 由於 當前鍵已使用,插入失敗 std::cout << "if successful insert: " << (bool)insert_result.second << ", insert place: " << (*insert_result.first).first << ",value: " << (*insert_result.first).second << std::endl; // 修改值 (*insert_result.first).second = "changed"; // emplace 與insert功能相同,只是參數不用使用std::make_pair auto emplace_result = b.emplace(14,"emplace"); for (const auto& item : b){ std::cout << item.first << " " << item.second << std::endl; } // 查找值 //可以使用find(key)進行查找 auto find_res = b.find(14); if (find_res != b.end()){ // 查找成功 std::cout << "found\n"; } else{ std::cout << "not found\n"; } // 遍歷 希望遍歷時修改值的值,可以使用這種方法,平時可以使用前面的方法 for (auto item = b.begin();item != b.end();++item){ item->second += "vvvvv"; std::cout << "key: " << item->first << ", value: " << item->second << std::endl; } } 結果 use [] 2 insert if successful insert: 0, insert place: 12,value: init 14 emplace 12 changed found key: 14, value: emplacevvvvv key: 12, value: changedvvvvv
-
可以寫一個模板函數來跳過[]查找引發的問題
template <class Map> typename Map::mapped_type inline get_default( const Map& _map, const typename Map::key_type& key, const typename Map::mapped_type& dlft = typename Map::mapped_type() ) { auto pos = _map.find(key); return (pos != _map.end() ? pos->second : dlft); } 使用 auto p = get_default(g,12); auto p = get_default(g,12,"default");
-
一個使用自定義鍵的例子
class Position{ private: int m_x = 0; int m_y = 0; public: int x() const { return m_x;} int y() const { return m_y;} Position(int _x,int _y) : m_x(_x),m_y(_y) {}; // equal_to bool operator==(const Position& op) const { return m_x == op.m_x && m_y == op.m_y;} }; // 定義hash函數 template <class T> inline void hash_combine(std::size_t &seed, const T &v){ std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } // Position特定模板函數 namespace std{ template <> struct hash<Position>{ size_t operator()(const Position& p) const{ auto key = hash<int>()(p.x()); hash_combine(key,p.y()); return key; } }; } class Person{ private: int m_age; std::string m_name; public: Person():m_age(0),m_name(){} Person(int _age,std::string _name) : m_age(_age),m_name(std::move(_name)){}; int age() const { return m_age;} std::string name() const { return m_name;} bool empty(){ return m_age == 0 && m_name.empty();} void print(){std::cout << "age: " << m_age << ", name: " << m_name << std::endl; } }; template <class Map> typename Map::mapped_type inline get_default( const Map& _map, const typename Map::key_type& key, const typename Map::mapped_type& dlft = typename Map::mapped_type() ) { auto pos = _map.find(key); return (pos != _map.end() ? pos->second : dlft); } void testUnorderedMap(){ using Group = std::unordered_map<Position,Person>; Group a{{Position(1,2),Person(12,"adsf")}}; auto result = a.insert(std::make_pair(Position(22,2),Person(22,"adddsf"))); Group g(a.begin(),a.end()); auto p = get_default(g,Position(1,2)); if (p.empty()){ std::cout << "not found" << std::endl; } else { p.print(); } } int main(){ testUnorderedMap(); return 0; } 結果 age: 12, name: adsf