结论如下:
Release模式下:
1. 容量为10的时候,查找效率:map > unordered_map > hash_map
2. 容量为100的时候,查找效率:map = unordered_map > hash_map
3. 容量为1000的时候,查找效率:unordered_map > hash_map > 4倍map
4. 容量为1万的时候,查找效率:hash_map > unordered_map > 4倍map
5. 容量为10万的时候,查找效率:hash_map > unordered_map > 4倍map
6. 容量为100万的时候,查找效率:hash_map > unordered_map > 1.6倍map
7. 容量为1000万的时候,查找效率:hash_map > unordered_map > 1.4倍map
8. 容量为1亿的时候,程序崩溃了
9. debug模式下和release模式下,差距非常大,几百倍的差距。。。。。
Debug模式下:
1. 查找效率:hash_map > unordered_map > map
2. 随着容量的增加,hash_map, unordered_map的查找效率有所降低,但浮动不大毕竟是常量级别。map的效率直线下降。。。
3. 容量为一千万的时候,程序同样崩溃
实验结果如下图:
Release模式 Debug模式(注意:相比Release模式还降低了10倍的查询量)
代码如下:
#include<iostream>
#include<map>
#include<hash_map>
#include<unordered_map>
#include <functional>
using namespace std;
uint64_t gettimeW()
{
uint64_t t;
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint64_t) ti.tv_sec * 1000000000;
t += ti.tv_nsec;
return t;
}
// 测试函数,
// size : map的实际大小
// times : 查找的轮次,每一轮次都从0查找到size-1
void test(int size, int times)
{
cout << "size=" << size << "; times=" << times << endl;
auto cur = gettimeW();
map<int, int> m;
auto sub = gettimeW() - cur;
printf("map create: custrom %ld \n", sub);
cur = gettimeW();
unordered_map<int, int> um;
sub = gettimeW() - cur;
printf("unmap init: custrom %ld \n", sub);
cur = gettimeW();
// 初始化
for (int i = 0; i < size; i++)
{
m[i] = i;
}
sub = gettimeW() - cur;
printf("map init: custrom%ld \n", sub);
cur = gettimeW();
for (int i = 0; i < size; i++)
{
um[i] = i;
}
sub = gettimeW() - cur;
printf("unmap init: custrom%d \n", sub);
// map的查找
{
int count = 0;
auto cur = time(nullptr);
for (int i = 0; i < times; i++)
{
for (int j = 0; j < size; j++)
{
if (m.find(j) != m.end())
{
count++;
}
}
}
auto sub = time(nullptr) - cur;
printf("map count: %d, custrom%d \n", count, sub);
}
// unordered_map的查找
{
int count = 0;
auto cur = time(nullptr);
for (int i = 0; i < times; i++)
{
for (int j = 0; j < size; j++)
{
if (um.find(j) != um.end())
{
count++;
}
}
}
auto sub = time(nullptr) - cur;
printf("ordermap count: %d, custrom%d \n", count, sub);
}
}
void testMapMem(int size)
{
map<int, int> m;
// 初始化
for (int i = 0; i < size; i++)
{
m[i * 2] = i;
}
int count = 0;
for (int j = 0; j < size; j++)
{
if (m.find(j) != m.end())
{
count++;
}
}
getchar();
}
void testUnorderMapMem(int size)
{
unordered_map<int, int> um;
// 初始化
for (int i = 0; i < size; i++)
{
um[i * 2] = i;
}
int count = 0;
for (int j = 0; j < size; j++)
{
if (um.find(j) != um.end())
{
count++;
}
}
getchar();
}
typedef function<void(int, int)> bindc11;
int main()
{
bindc11 t;
printf("size; %d \n", sizeof(t));
// test(10,50000000); // 容量:10 查找:1千万轮次
// test(100, 1000000); // 容量:100 查找:1百万轮次
//
test(10000, 100000); // 容量:1000 查找:10万轮次
//
// test(10000, 10000); // 容量:10000 查找:1万轮次
//
// test(100000, 1000); // 容量:100000 查找:1000轮次
//
// test(1000000, 100); // 容量:1000000 查找:100轮次
//
// test(10000000, 10); // 容量:10000000 查找:10轮次
// testMapMem(100000);
// testUnorderMapMem(100000);
// getchar();
return 0;
}
release模式参照上面
但是debug模式下,
4.8.5编译器结果 map 100 以下更快其他, unordermap 更快
8.0的任何大小的容器都是 unordermap快
创建和初始化对比
100个元素的时候
map create: custrom 2082
unmap init: custrom 68
map init: custrom69716
unmap init: custrom36615
1000个元素
map create: custrom 1678
unmap init: custrom 92
map init: custrom942322
unmap init: custrom361346
10000个元素
map create: custrom 1275
unmap init: custrom 172
map init: custrom6359164
unmap init: custrom3515528
100000个元素
map create: custrom 1214
unmap init: custrom 67
map init: custrom75028499
unmap init: custrom26605541
消耗时间map都大于unorder_map
内存测试 8 编译器 ,大小100 - 1000 的时候都一样
10000 上面是map, 下面是undorder_map
100000
1000000
10000000
与map对比
map
优点:
有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作;
红黑树,内部实现一个红黑树使得map的很多操作在O(lgn)的时间复杂度下就可以实现,因此效率非常的高。
缺点:空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
应用场景:对于那些有顺序要求的问题,用map会更高效一些
unordered_map
优点:因为内部实现了哈希表,因此其查找速度非常的快
缺点:哈希表的建立比较耗费时间
应用场景:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
总结
内存占有率的问题就转化成红黑树 VS Hash表,还是unordered_map占用的内存要高;
但是unordered_map执行效率要比map高很多;
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的。
unordered_map扩容原理
unordered_map 内部实现是散列表,是一个无序的容器。内部实现的散列表采用了链地址法,意思是使用链表来解决散列冲突。当往容器中加入一个元素的时候,会计算散列值,然后取余之后放到一个桶 (bucket) 里。如果不断往容器加元素,那么所有的桶都会变成一个很长的链表,这样效率就很低了,这种情况应该如何避免呢?unordered_map 采用了扩容的机制,当负载因子 (load factor) 超过了阈值,就会进行一次扩容。负载因子的计算方法是总键值对数除以桶数。扩容的时候,会重新计算散列,重新放入到桶里。
还要评估: 创建耗时和再扩容