向量或地图,使用哪一个?


71

我听过很多人说,如果容器中期望的元素数量相对较少,那么最好使用std::vector而不是std::map使用容器,即使您仅将容器用于查找而不是迭代。

这背后的真正原因是什么?

显然,的查询性能std::map不会比std::vector(尽管可能会相差十亿分之一秒/微秒)差,但是它与内存使用情况有关吗?

是否std::vector比分std::map段虚拟地址空间更好/更糟糕?

我正在使用Visual Studio附带的STL库(即Microsoft的实现)。与其他实现相比,这有什么区别吗?

Answers:


74

我相信你比较map<A, B>vector<pair<A, B> >

首先,在很小的向量中查找项目可能比在地图中查找相同对象要快,因为向量中的所有内存始终是连续的(因此可以更好地与计算机的缓存等配合使用)以及数量在向量中查找某物所需的比较可能与地图大致相同。在非常大的容器范围内,在地图中查找元素所需的操作更少。

映射变得比矢量更快的时间点取决于实现,处理器,映射中包含哪些数据以及诸如处理器高速缓存中的内存之类的细微事物。通常,地图变快的点约为5-30个元素。

一种替代方法是使用哈希容器。它们通常被命名为hash_mapunordered_map。命名hash_map的类不是官方标准的一部分(那里有一些变体);std::tr1::unordered_map是。哈希映射通常比普通映射查找要快,无论其中有多少个元素,但实际上是否更快取决于密钥是什么,如何对其进行哈希处理,必须处理什么值以及如何密钥在std :: map中进行比较。它不会按std :: map这样的特定顺序进行操作,但是您已经说过您对此并不在意。我建议使用哈希映射,尤其是在键是整数或指针的情况下,因为这些哈希非常快。


2
奇怪的是,我发现Java的HashMap比C ++ Map快得多。您帖子的最后一段可能描述了原因。
wmac

5
@wmac:右图:这是更准确的Java的比较HashMap,以C ++hash_mapunordered_map和Java的SortedMap,以C ++ map
Mooing Duck 2015年

5
当我进行基准测试时,我发现std :: map与std :: vector的步调大约为8000,但在某些硬件上低至1000,我使用的代码可在以下网址
Sqeaky

28

映射通常以二进制搜索树的形式实现,遍历二进制树总是会带来一些开销(执行比较,遍历链接等)。向量基本上只是数组。对于非常少量的数据(可能是8或12个元素),有时对数组进行线性搜索要比遍历二进制搜索树更快。

您可以自己进行一些计时,以查看收支平衡点在哪里–对四个元素进行搜索,然后依次搜索八个,然后十六个,以此类推,以找到针对您的STL特定实现的最佳结合点。

映射的确确实会在整个堆上分配一堆小的分配,而向量是连续的,因此,当您从头到尾遍历所有元素时,向量的缓存命中率有时会更好。


5
您甚至不必进行线性搜索。std :: lower_bound使您可以对任何排序的容器进行二进制搜索。当有很多键插入会改变搜索树的结构时,Map很有用。如果它是一个相当静态的集合,则排序后的向量和lower_bound会很容易地匹配map中的性能,而不仅仅是几个元素。当然在实践上仍然值得比较!
Zoomulator

26

“默认情况下,在需要容器时使用矢量”-Bjarne Stroustrup。

否则,我觉得这个小流程图会很有帮助(已编辑-可能是有效的实时新链接):

https://ngoduyhoa.blogspot.com/2015/06/summary-of-different-containers.html


6
根据Herb Sutter(gotw.ca/gotw/054.htm)的说法,考虑到双端队列和向量之间的选择,通常最好选择双端队列。
graham.reeds 09-10-22

3
deque很不错,因为它几乎和向量一样快,但是由于deque的块是独立分配的,因此不需要为了增加而移动所有内容。
Zan Lynx

4
链接似乎已死...您还有其他来源吗?
kebs

确实死了。这似乎是原始图表(待确认):ngoduyhoa.blogspot.com/2015/06/…–
亚历克斯

6

如果您一次完成所有插入操作,然后进行大量查找,则可以在插入过程中使用向量对它进行排序。然后使用lower_bound进行快速查找。即使对于大量项目,它可能比使用地图更快。


3

我认为您应该首先使用适合数据的容器。std :: vector用于在C或STL之前的C ++中使用数组的情况:您希望连续的内存块以快速的恒定时间查找来存储值。应该使用std :: map将键映射到值。这里的主要重叠是向量与以size_t为键的映射。在那种情况下,有两个问题:索引是连续的吗?如果没有,您可能会浪费向量。其次,您想要什么查询时间?向量具有恒定的时间查找,而std :: map通常实现为RB树,具有O(log n)查找时间,甚至哈希映射(例如TR1 unordered_map)通常也具有更差的复杂度,因为索引(或其哈希)将映射到可以包含多个值的存储桶。

如果目标是成对的向量:则可以使用向量的元素并使用find来查找元素。但这是一个二进制搜索,实际上将和std :: map一样快。

无论如何,请尝试以明显的方式对数据建模。过早的优化通常并没有太大帮助。


3

另一种看待这种情况的方式是,如果我们谈论的是小容器,那么没人会花很长时间来查找。除非您在非常紧密的循环中搜索此容器,否则时间差异可能可以忽略不计。

在这种情况下,我将寻找哪个容器更符合您的需求。如果您要查找特定值,则映射的内置find()方法比创建for循环并遍历向量要容易得多(使用起来也不太复杂)。

您所花费的时间可能比在这里和那里花费的时间多得多。


是的,我同意节省CPU时间是不值得的。但是内存消耗呢?
Naveen

4
我通常都同意,但是请注意std :: find()算法在地图和矢量上均能非常愉快地运行。
j_random_hacker

1
如果我们谈论的是少量条目,那么总体上的内存消耗将很低……几字节?我们在这里谈论什么...二十岁?Map具有内置的查找功能,比std :: find()容易一些。
teeks99

0

基本上,地图用于查找。

但是,有时甚至std::vector可以std::map用于查找。

如果键值对中的元素要少得多,那么即使在中也可以使用key进行迭代搜索std::vector<std::pair<x,y>>

这是因为散列会花费时间,尤其是散列字符串和地图中的其他操作(例如将数据存储在堆中)的情况。

如果您有更多要查找的元素以及想要在元素列表中进行频繁查找的元素,则只会在std :: map中看到更好的区别。


2
std :: map不使用哈希进行查找。默认情况下,它使用std :: less作为比较器
rmawatson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.