vector、map还是unordered_map?

2023-05-16


一、引言      

    当我们需要使用键值对的情况时,通常我们会使用map或者unordered_map。其中map底层是采用红黑树实现的,它的查询复杂度是O(lgn);unordered_map实际上是hash_map的实现,理论上它的查询复杂度是O(1)的。那么当我们需要使用键值对结构时,我们是否就一定需要使用它们,或者说它们就一定是最好的选择呢?  

    答案是否定的,实际上我们可以使用元素为std::pair的vector对象来实现键值对。那么这三者之间究竟有什么区别呢?我们应该如何在这三者之间进行抉择呢?接下来我们从内存和访问效率两个方面分别来看这三者的区别。  

二、内存使用  

  由于我们无法直接获取stl容器所使用的内存大小,所以我们这里采用从任务管理器中查看进程的方式来比较三种方式内存消耗情况(当然,可以采用第三方库来查看程序内存消耗情况,此处我们只是做近似的估计,所以直接采用任务管理器查看进程内存消耗情况)。我们比较存储1000万个<int, int>键值对时,三者内存消耗的大小:  

 
 
方式消耗内存大小
vector<pair<int,int> >
8,204KB
map<int, int>
47,336KB
unordered_map<int, int>
47,748KB
从表中可以看到,内存消耗从小到大依次是:vector >> map >> unordered_map.其中map所使用的内存是vector的6倍,而unordered_map的内存消耗是map的1.1倍。

    当然,以上只是存储int这种小对象,我们继续看看当存储的对象较大时,三者所占的情况。下表给出的结果结果是存储100万个<int, Test>键值对,三者所占内存的情况,其中Test对象包含一个大小为20个double数组:

  
  
方式消耗内存大小
vector<pair<int,Test> >
321,326KB
map<int,  Test>
360,452KB
unordered_map<int,  Test>
360,928KB
从上表中可以看到,当存储的对象增大时,vector的优势变得并不明显。因为随着对象增大,用于存储对象的空间所占的比例越来越大。因此单从内存消耗的角度来看,当存储的是小对象时,vector占很大的优势。但是,当存储的对象本身大小增大时,它的优势变得不再那么明显。

三、查询效率  

    说到查询效率,在vector无序的情况下,它的查询复杂度是O(n)的,所以当数据量稍大时,查询效率是十分低下的。但如果vector的元素有序的情况又如何呢?我们通过如下代码进行测试,其中三个容器都包含10000个<int, int>键值对元素,我们进行100万次查询,并对比它们的耗时情况:  

  
void testMap(map<int, int> & mapT)
{
	for (int i = 0; i < 1000000; ++i)
	{
		int key = rand() % 10000;
		mapT[key] += 1;
	}
}
void testHashMap(unordered_map<int, int> & mapT)
{
	for (int i = 0; i < 1000000; ++i)
	{
		int key = rand() % 10000;
		mapT[key] += 1;
	}
}
void binaryFind(vector<pair<int, int>> & vecT)
{	
	bool n = false;
	static auto comparerer = [](int key, pair<int, int>& keyVal)
	{
		return key == keyVal.first;
	};
	for (int i = 0; i < 1000000; ++i)
	{
		int key = rand() % 10000;
		auto iter = std::upper_bound(vecT.begin(), vecT.end(), key, comparerer);
		iter->second += 1;
	}
}
int main()
{
	{
		map<int, int> mapT;
		for (int i = 0; i < 10000; i++)
		{
			mapT[i] = i;
		}
		auto nowClock = clock();
		testMap(mapT);
		cout << "map cost " << (clock() - nowClock) << "ms" << endl;
	}
        {
	     unordered_map<int, int> mapT;
	     for (int i = 0; i < 10000; i++)
	     {
		 mapT[i] = i;
	     }
	     auto nowClock = clock();
	     testHashMap(mapT);
	     cout << "unordered_map cost " << (clock() - nowClock) << "ms" << endl;
         }
         {
	     vector<pair<int, int>> vecT;
	     for (int i = 0; i < 10000; i++)
		 vecT.push_back(pair<int, int>(i, i));
	     auto nowClock = clock();
	     binaryFind(vecT);
	     cout << "vector cost " << (clock() - nowClock) << "ms" << endl;
        }
	getchar();
	return 0;
}


  

    以上代码的运行结果如下表所示:  

  
  
方式运行时间
vector<pair<int,int> >
46ms
map<int, int>
157ms
unordered_map<int, int>
43ms
从上表可以看到,有序的vector采用二分查找时,查询效率与unordered_map的效率是相当的。而map的查询效率是vector的3.4倍。故而尽管理论上map的查询效率也是O(lgn),但是它仍然别有序的vector采用二分查找的效率低许多。

五、关于unordered_map的空间利用率问题的考虑  

    从以上的内存占用和访问效率的情况看,似乎unordered_map永远都是最佳的选择。但是在下这个结论之前,我们先来看考虑一种情况,我们有类A,这个为我们的真正存数据的类。一个Record类,它用于存储A,其中需要以一个int类型的值为key(类型不会太多,假设最多只有20个),表示不同类型的A类对象;一个Container类,它用一个vector存储类Record的对象。其中Record存储类A的对象可以用vector,也可以用unordered_map。代码如下:  
class A
{
public :
    double data[20];
};

class Record
{
public:
    unordered_map<int, A> record; // 或者 vector<pair<int, A>> record;

};

class Container
{
public:
    vector<Record> recorList;
};

    对于Record中选择vector或者unordered_map,占用的内存究竟会有多大区别呢?这里我们在Container中插入10万个Record对象进行对比,一下为对比结果:  

  
  
方式
消耗内存大小
vector<pair<int, A>>
334,732k
unordered_map<int, A>
469,196k

    从结果上看,使用unordered_map是vector的1.4倍,而且当我们的对象A的大小减小,这一比值会急剧攀升。假如A中只包含一个double,那对比结果如下:  

  
  
方式
消耗内存大小
vector<pair<int, A>>
36,528k
unordered_map<int, A>
155,824k
可以看到unordered_map的内存消耗是vector的近5倍,所以这种情况如果维护Record中的数据有序的成本不高的话,应该使用vector,而非unordered_map。

六、如何选择  

    当我们需要使用键值对时,我们应该综合考虑内存和效率两方面。以下是本人的一些个人建议:  

    1.元素为pair的vector  

  

    1)当vector不会频繁的在中间或者列表头插入、删除,且列表有序维护的成本低时,可以考虑使用vector,并利用二分查找来查找指定元素;  

    2)当数据量较大,内存不足以使用unordered_map时,应当使用vector替代;  

    2.map  

    1)当数据量不大,并且key的类型无法计算hash值,可以考虑使用map替代vector,以将我们从vector列表有序的维护中解放出来;  

    2)当数据量不大,且我们需要有序的访问键值对时,可以考虑使用map替代unordered_map;  

    3.unordered_map  

    1)在内存允许并且我们不要求有序的访问所有元素的情况下,我们应该尽量使用unordered_map;  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vector、map还是unordered_map? 的相关文章

  • 计算结构向量中的匹配项

    我有一个问题 要求我计算该数组中使用以下任一方法的实例的数量std count or std find 我知道如何使用标准数据 参见底部代码 类型来执行此操作 但不知道如何使用NameContainer我正在使用的 Type struct
  • 查找向量中最接近的值

    我想要完成的是迭代双精度值向量并返回最接近的可能双精度值的向量位置 我对此有两个问题 当尝试使用以下命令查找向量中最接近的双精度值时lower bound 如果我输入 1 我只会收到非零的值 我不知道如何使用lower bound返回向量位
  • 如何通过字符串名称访问结构体属性?

    我有一个结构 typedef struct Tick double open double high double low double close double ema100 Tick 我想访问给定密钥的属性 Tick currentTi
  • Octave/Matlab:向向量添加新元素

    有一个向量x我必须添加一个元素 newElem 有什么区别吗 x end 1 newElem and x x newElem x end 1 newElem更稳健一些 x x newElem 仅当x是行向量 如果它是列向量x x newEl
  • 如何从矩阵的每一行中减去一个向量? [复制]

    这个问题在这里已经有答案了 可能的重复 如何将矩阵的每一行除以固定行 https stackoverflow com questions 4723824 how can i divide each row of a matrix by a
  • 无法在 Android 中将矢量可绘制对象转换为位图可绘制对象

    我正在尝试将位图转换为字节数组 其中我将矢量可绘制图像转换为位图 然后将其转换为字节数组 但是当我打开应用程序时 它向我显示错误类强制转换异常无法将矢量可绘制图像转换为位图可绘制 Resources res getResources Dra
  • Numpy 提取网格数据的子集

    在我的应用程序中 我有一个从 meshgrid 命令获得的值矩阵及其坐标 经度 纬度 我想根据经度和纬度限制提取该矩阵的特定子区域 我已经尝试过这个解决方案 但它不起作用 我需要三个矩阵作为输出 一个用于数据 另外两个用于网格 Lons L
  • 如何在 C++ 中将值从向量转换为映射?

    我想做这样的事情 有没有一个stl算法可以轻松做到这一点 for each auto aValue in aVector aMap aValue 1 如果您有一个对向量 其中对中的第一项将是映射的键 第二项将是与该键关联的值 您可以使用插入
  • rbind 命名向量到不同长度的矩阵

    我正在尝试将命名向量绑定到矩阵上 命名向量的长度与矩阵不同 gt m lt matrix data c 1 2 3 nrow 1 ncol 3 dimnames list c c column 1 column 2 column 3 gt
  • 正确别名向量

    我无法在其他地方找到答案 所以我想我只需要问这个 我正在尝试获取向量 其中存储 int 指针 的别名 如下所示 void conversion Engine ENGINES The Engine class has a vector of
  • 将 R 中的向量按特定顺序转换为下三角矩阵

    我有一个向量 其中元素的顺序很重要 比如说 x lt c 1 2 3 4 我想将我的向量排列成具有特定顺序的下三角矩阵 其中每行包含向量的前一个元素 我的目标是获得以下矩阵 lower diag matrix 1 2 3 4 1 4 0 0
  • 使用步骤 c++ 构建向量

    是否可以在不使用 C 中的循环的情况下以固定步骤创建从一个值到另一个值的向量 例如 我想用步长 0 5 构建一个从 1 到 10 的向量 在 MATLAB 中我可以按如下方式执行此操作 vector 1 0 5 10 c 中有类似的东西吗
  • 从向量中删除向量::end

    当我使用时它工作正常吗 什么也不做 vector
  • 错误 C2039:“find”:不是“std”的成员

    我刚刚遇到一个奇怪的错误 它说 find 不是 std 的成员 错误 C2039 find 不是 std 的成员 错误 C3861 查找 未找到标识符 基本上 我想查找是否可以在向量中找到字符串 知道为什么会发生这种情况吗 代码帮助告诉我
  • 如何逐步绘制矢量路径? (拉斐尔.js)

    如何逐步动画化矢量路径 就像它被绘制一样 换句话说 慢慢地逐像素地显示路径 我在用着Rapha l js but如果您的答案不是特定于库的 例如可能有一些通用的编程模式可以完成此类事情 我对矢量动画相当陌生 欢迎 使用直线路径很容易做到 就
  • 我应该如何格式化 .dat 文件以便制作 3D 矢量图?

    我正在为大学做这个编程任务 我们必须写一个c 计算 3D 空间中某些线圈的磁场矢量的程序 我已经成功编写了这个程序 并且我认为它运行得很好 不过 我想添加一个特殊的东西 这是我的试卷 所以它必须特别好 我想绘制出向量 我习惯打电话gnupl
  • 确定向量中是否存在元素的最有效方法

    我有几种算法取决于确定元素是否存在于向量中的效率 在我看来 这 in 这相当于is element 应该是最有效的 因为它只返回一个布尔值 在测试了几种方法之后 令我惊讶的是 这些方法是迄今为止效率最低的 以下是我的分析 随着向量大小的增加
  • 如何将带有自定义分配器的 std::vector 传递给需要带有 std::allocator 的函数?

    我正在使用外部库 pcl 因此我需要一个不会更改现有函数原型的解决方案 我正在使用的一个函数生成一个std vector
  • 删除向量类成员

    我有一个 A 类 其成员是另一个 B 类的对象指针向量 class A std vector
  • 用数组或向量实现多维数组

    我想使用单个数组或向量实现多维数组 可以像通常的多维数组一样访问它 例如 a 1 2 3 我陷入困境的是如何实施 操作员 如果数组的维数为 1 则 a 1 应该返回位于索引 1 处的元素 但是如果维数大于一怎么办 对于嵌套向量 例如 3 维

随机推荐