选择策略
大多数情况下,应优先考虑使用unordered_map
,除非使用场合要求key是有序的。
区别
map
和unordered_map
在使用接口上非常像,但是两者的实现方式不同,决定了两者的表现形式和性能是不一样的。
map
使用红黑树实现,特点是有序,查找效率是O(log(n)),关于map
更详细的使用和数据结构的组织可以看这里,下边是一个示意结构(仅显示key值)。
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
unordered_map
使用hash map组织元素,因此key是无序的,查找效率是O(1)。还是上边的key值,简单起见我们以H(key) = key MOD 8
为hash函数,则下边是其组织结构的示意图:
|-8
|-1
|-10
|-3
|-4
|-13
|-6-14
|-7
关于效率
对于上边的这个例子,我们发现unordered_map
的查找效率确实要比map
快很多,比如我们查找key=7的元素,在map中我们要比较四次,分别是8 3 6 7,才可以得到想找的位置。可以对于unordered_map
,我们直接取8的余,就得到了。
所以,这两个效率的比较是O(1)与树的高度的比较,当然这个效率不代表所有情况,例如对于14这个元素两者分别进行了三次和两次比较。
在更极端情况下,如果我们插入的元素的hash值都是一样的,那么,unordered_map
的查找效率反而要坏于map
了,例如下边的这种情况。
|-8-16-24-32-40-48-56
|
|
|
|
|
|
|
有序与无序
可以看出,对于map
来说,无论元素添加的顺序是怎样的,在进入map之后都会进行排序,严格来说是有序的插入。
unordered_map
确是按照元素插入的顺序进行输出,即访问的。那么是否意味着只要有序的插入,就可以保证unordered_map
有序的组织元素呢?答案是否定的,这种按照插入顺序的顺序进行的访问和输出,某种意义上只是一种巧合。严格说来,unordered_map
的访问顺序是由采用的hash函数决定的。
#include <stdio.h>
#include <map>
#include <unordered_map>
#include <string>
#include <utility> // used for pair
using std::map;
using std::unordered_map;
using std::string;
using std::pair;
int main()
{
map<int, string> map_s;
map_s.insert(pair<int, string>(2, "zhao"));
map_s.insert(pair<int, string>(1, "qian"));
map_s.insert(pair<int, string>(3, "sun"));
printf("std:map:\n");
for (auto& v : map_s)
{
printf("\t%d:%s\n", v.first, v.second.c_str());
}
unordered_map<int, string> map_us;
map_us.insert(pair<int, string>(2, "zhao"));
map_us.insert(pair<int, string>(1, "qian"));
map_us.insert(pair<int, string>(3, "sun"));
printf("unordered_map:\n");
for (auto& v : map_us)
{
printf("\t%d:%s\n", v.first, v.second.c_str());
}
return 0;
}
以上代码的输出如下,
std:map:
1:qian
2:zhao
3:sun
unordered_map:
2:zhao
1:qian
3:sun