map 和 unordered_map 使用实践

map 和 unordered_map 使用实践

0. 简介

mapunordered_map 的对于用户的使用方式上是相同的,都是一个key, value的查找表。但是在使用场景和效率上有所不同。

1. map 和 unordered_map 不同点

1.1 使用场景不同

map 是内部有序的,按照key的值进行从小到大排序(可以自定义排序方式)。所以可以通过获取首个元素获取key值最小的对应的value值,或者通过find找到对应key值相邻大小的<key,value>。常用方法可见示例。

unordered_map内部是无序的,所以对于用户来说是一个纯粹的查找表:根据key值找到对应的vlue.

1.2 性能与资源消耗不同

通常情况下,map 的平局算法复杂度 O(logN), un_ordered_map 的算法复杂都O(1)。但是un_ordered_map 因为是散列存储,所以消耗的存储资源要比 map 高(线性比例)。
另外,如果是频繁使用插入和删除等操作的时候,性能消耗将会增加。

1.3 实现原理不同

map: 内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。
unordered_map: 内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的

1.4 使用 map or unordered_map ?

根据stack overflow 上的讨论 Is there any advantage of using map over unordered_map in case of trivial keys? 总结几点使用map的场景如下:

  1. 如果需要key的排序功能场景,使用map
  2. 频繁的插入,删除节点的场景,使用map

否则在使用 unordered_map 效率会更高。

2 使用典型示例

2.1 map

模板结构:

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T>>
> class map;
2.1.1 默认有序存储
#include <iostream>
#include <map>
#include <memory>
void separator() { std::cout << std::endl << "------" << std::endl; }

int main() {

  // 构建map <1, "first">, <2, "second">, <3, "third">, <5, "fourth">
  std::map<int, std::string> m_map;
  m_map.emplace(2, "second");
  m_map.emplace(1, "first");
  m_map.emplace(5, "fourth");
  m_map.emplace(3, "third");

  // 自动按key从小到大排序(而不是数据插入顺序): first second third fourth
  for (auto it = m_map.begin(); it != m_map.end(); it++) {
    std::cout << it->second << " ";
  }
  separator();

  // 获取最小的key的value值,预期打印: <1, "first">
  std::cout << m_map.begin()->first << "," << m_map.begin()->second;
  separator();

  // 获取最大的key的value值,预期打印: <5, "fourth">
  auto max_iter = m_map.end();
  max_iter--;
  std::cout << max_iter->first << "," << max_iter->second;
  separator();

  // 获取key不大于4的最大值,预期打印: <3, "third">
  auto iter = m_map.upper_bound(4);
  if (iter != m_map.begin()) {
    iter--;
    std::cout << iter->first << "," << iter->second;
  } else {
    std::cout << "not found.";
  }
  separator();

  return 0;
}

2.1.2 自定义比较器
// 构建自己的比较器,如果类型确定,可以不使用模板,此处定义从大到小的排列
template<class T>
struct MyGreaterCompare {
  constexpr bool operator()(const T &lhs, const T &rhs) const {
    // 如果是类可以自己定义
    return lhs > rhs;
  }
};

// 构建map <5, "fourth">, <3, "third">, <2, "second">, <1, "first">
  std::map<int, std::string, MyGreaterCompare<int>> m_map;
  m_map.emplace(2, "second");
  m_map.emplace(1, "first");
  m_map.emplace(5, "fourth");
  m_map.emplace(3, "third");

  // 自动按key从小到大排序(而不是数据插入顺序): fourth third second first
  for (auto it = m_map.begin(); it != m_map.end(); it++) {
    std::cout << it->second << " ";
  }

2.2 unordered_map

模板结构

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator<std::pair<const Key, T>>
> class unordered_map;
2.2.1 无序存储
#include <iostream>
#include <memory>
#include <unordered_map>

void separator() { std::cout << std::endl << "------" << std::endl; }

int main() {
  // 构建map <2, "second">, <1, "first">, <5, "fourth">, <3, "third">
  std::unordered_map<int, std::string> m_map;
  m_map.emplace(2, "second");
  m_map.emplace(1, "first");
  m_map.emplace(5, "fourth");
  m_map.emplace(3, "third");

  // 无序,数据插入顺序: second first fourth third
  for (auto it = m_map.begin(); it != m_map.end(); it++) {
    std::cout << it->second << " ";
  }
  separator();

  // 获取bucket 个数
  std::cout << m_map.bucket_count();
  separator();
  return 0;
}
2.2.2 自定义hash函数

引用std::hash

#include <cstddef>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_set>
 
struct S {
  std::string first_name;
  std::string last_name;
  // 此处可以自定义比较器
  bool operator==(const S &rhs) const noexcept{
    return first_name == rhs.first_name && last_name == rhs.last_name;
  }
  S(std::string first, std::string second):first_name(first),last_name(second){}
};

struct MyHash {
  std::size_t operator()(const S &s) const noexcept {
    std::size_t h1 = std::hash<std::string>{}(s.first_name);
    std::size_t h2 = std::hash<std::string>{}(s.last_name);
    return h1 ^ (h2 << 1); // or use boost::hash_combine
  }
};
 
int main()
{
  //定义时候传入hash函数对象
  std::unordered_map<S, std::string, MyHash> m_map;
  m_map.emplace(std::piecewise_construct, std::forward_as_tuple("b", "bb"),
                std::forward_as_tuple("second"));
  m_map.emplace(std::piecewise_construct, std::forward_as_tuple("a", "aa"),
                std::forward_as_tuple("first"));
  // 无序,数据插入顺序: second first fourth third
  for (auto it = m_map.begin(); it != m_map.end(); it++) {
    std::cout << it->second << " ";
  }
  separator();

  // 获取bucket 个数
  std::cout << m_map.bucket_count();
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
map和unordered_mapC++标准库中的两种关联容器,它们的使用场景有所不同。 map底层使用红黑树结构,保证了元素的有序性。因此,在需要元素有序性或对单次查询性能要求较高时,可以使用map。例如,需要按照键的自然顺序进行遍历、查询或者需要在有序的键序列中进行范围查找等情况下,map是一个不错的选择。 unordered_map底层使用哈希结构,它不保证元素的有序性,但对于查找性能来说更高效。因此,在不需要元素有序性或对单次查询性能要求不那么敏感的场景下,可以使用unordered_map。例如,需要快速的元素查找、插入和删除操作时,unordered_map会更适合。 总结来说,在需要元素有序性或对单次查询性能要求较高的情况下,可以使用map。而在不需要元素有序性或对单次查询性能要求不那么敏感的情况下,可以使用unordered_map。在算法编程的大部分情况下,更倾向于使用unordered_map而不是map。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [浅谈map和unordered_map的应用场景](https://blog.csdn.net/wtl666_6/article/details/128532718)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++小知识——map和unordered_map区别](https://blog.csdn.net/qq_45890970/article/details/123955261)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值