C++ Primer学习笔记(11)——(转)c++ 标准库的各种容器(vector,deque,map,set,unordered_map,unordered_set,list)的性能考虑

转载 2015年07月08日 10:01:39

声明:此文章转自truexf 的博客,自觉他总结的比我好,故收藏于此,共学习之用。谢谢大神分享!

一、vector

vector采用一段连续的内存来存储其元素,向vector添加元素的时候,如果容量不足,vector便会重新malloc一段更大的内存,然后把原内存中的数据memcpy到新的内存中,并free原内存块,然后将新元素加入。vector的元素插入性能跟以下几个要素关系重大:

1.插入的位置

头部插入:将所有元素后移,然后将新元素插入

中间插入:将插入点后面的元素后移,然后插入新元素

尾部插入:将新元素直接插入尾部

尾部插入无疑是最快的,头部插入最慢,中间插入次之,慢的点在于插入前要移动内存。

删除元素也是同样的道理。

2.保留空间大小

如果插入元素是,空间不足将导致重新malloc以及一系列的内存拷贝。如果使用者能对容量有预期,那么采用reserve()来预先分配内存,将大大的提高性能。

3.内存扩展算法的库实现相关

在空间不足导致需要重新malloc的时候,不同的库实现有很大的不同,往往特定平台的实现会结合操作系统的平台特性以及malloc算法,提供相当优秀的内存扩展算法,在百万次vector的插入操作中,不提供reserve()的情况向,性能表现非常优秀。接近于使用了reserve()的情况。

因为vector采用连续的内存存储其元素,因此其支持元素的下标法随机访问,且时间复杂度是常量0;

如果是查找元素,vector的find()成员函数对元素进行查找时是采用从头到尾扫描的方式,他时间复杂度是O(n),如果vector要应付查找的性能需求,那么应该采取排序的vector,利用算法库的getlowerbound()进行元素的有序插入,利用binary_search()对元素进行二分查找。这种情况下其查找性能不输于基于红黑树的set和map,更是令list望尘莫及。

综述,vector适用于尾部插入,但是此时无法兼顾查找的性能,因为二分查找的vector要求重新排序,或者要求vector在插入时就保持有序,这样就无法做到尾部插入。

但是vector作为动态数组的替代,已经足够优秀。

二、deque

deque采用多块内存串起来的方式提供其元素的存错,每一个内存块存储多个元素,每一块内存存储的元素个数相同,这是他不同于vector采用一块内存来存错所有的元素的方式。这样带来的好处是:

首先,头部插入和尾部插入/删除元素的成本是一样,弥补了vector再头部插入元素性能不佳的问题;

其次,对于vector的一个内存块的模式,当有巨大数量的元素,操作系统的大内存分配和赋值时很缓慢的,而且deque的方式就不会带来这个问题。

缺点是:

首先对元素的访问需要经过两个层次,第一次找到元素所在的内存块,第二次找到块中的元素。不过这个时间几乎是可以忽略,除非对性能要求极其苛刻。

其次,对其进行排序,以及排序后的查找会比较慢,想象知名的排序算法,都是针对一段连续内存进行下标访问,而deque是断续的内存块组成。同样排序后的折半查找也无法利用下标直接访问自然性能大打折扣。

三、list

list很简单,他就是个双向链表。每一个节点的内存都是独立的。理论上,其优点是任何位置的插入删除元素操作都是非常块的。缺点是不适合用于元素的查找,因为他只能是扫描的方式。根据实际的测试情况来看,我认为list不值得一用,因为节点的频繁新增与删除将导致大量重复的内存分配和释放操作。而实际上,操作系统以及运行库的内存分配与释放频率和策略才是影响stl各大容器的性能最关键的点。

四、map/multi_map/set/multi_set

这四个数据结构是采用平衡树(红黑树)实现其元素在内存中的存储。理论上(与list一样是理论上)他们的性能是很高,而且在插入/删除与元素查找上是的平衡点掌握的相当好的,具体的算法复杂度可参考红黑树算法文献,但是同样基于“操作系统以及运行库的内存分配与释放频率和策略才是影响stl各大容器的性能最关键的点”这一法则,他们在理论之外,实际的应用中,总是表现得不是太好。

五、散列容器
unordered_map/unordered_set/unordered_multi_map/unordered_multi_set

c++11引入的散列容器,散列容器具有不稳定性:他依赖于实际所使用的散列算法。而针对不同的元素数量,不同的散列算法具有相当大的性能差异。

所以,理论上(同样是理论上)他们的算法时间复杂度是近乎“常量”(如果要处理冲突那就不是了)。但时间上对于一般用户使用起来可能会带来风险。除非你对所使用的散列算法和元素数量都有很好的预估。在我一些简单的测试(采用int元素类型,采用std的默认散列函数)中,散列容器的性能是排名垫底的。

六、“内存分配与释放频率和策略才是影响stl各大容器的性能最关键的点”

这点非常重要。不同的操作系统,不同的运行库,他的虚拟内存管理算法,以及运行库的malloc和free的内部实现都是有差异的。如何找出最有效率的使用标准库容器的方式,一定要结合这一点进行大量的测试才能得出。

有一点可以确认的是:各个标准库的实现,已经是在遵循c++标准的基础上,在性能和适用性上可以说做到了极致。因此很多人总想自己造重复的轮子是大可不必。理解好各种容器的实现原理,再结合实际应用的需求,选择合适的容器,以及容器的使用方式,才是上上之策。

相关文章推荐

c++ 标准库的各种容器(vector,deque,map,set,unordered_map,unordered_set,list)的性能考虑

c++ 标准库的各种容器(vector,deque,map,set,unordered_map,unordered_set,list)的性能考虑   一、vector vector采用一段连续的...
  • truexf
  • truexf
  • 2013年12月13日 15:55
  • 12419

C++标准库:关联容器(set、map、unordered、multi)

set:std::set是一个关联容器,是一个有序的集合,集合中包含不可重复的、类型为Key的元素。排序通过使用类型为Compare的比较函数比较来实现。搜索,删除和插入操作具有对数时间复杂度。set...
  • we1meng
  • we1meng
  • 2017年05月11日 17:08
  • 133

C++各个容器比较(vector,deque,list,set,map,queue,stack)

1、vector(连续的空间存储,可以使用[ ]操作符)可以快速的访问随机的元素,快速的在末尾插入元素,但是在序列中间随机的插入、删除元素要慢。而且,如果一开始分配的空间不够时,有一个重新分配更大空间...

C++关联容器(unordered_map,vector,unordered_set)

说明书: http://classfoo.com/ccby/article/qNNOJ#sec_4Gxme0unordered_set:(无序集合)//500 class Solution { p...

C++ STL标准库的关联容器set与map

C++ STL标准库的关联容器set与map C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂...

C++/STL用erase删除元素(vector,deque),(list,set,map)

STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector 、deque);另一类是以不连续的节点形式存储的容器(如:list、set、map)。在使用erase方法来删除元素时...

C/C++--STL中list,vector,deque,map,set区别、联系和使用场景

原谅地址:http://www.lifecrunch.biz/archives/202 vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此 它...
  • zlQQhs
  • zlQQhs
  • 2014年06月16日 20:46
  • 2963

C++中vector, list, deque, set, map详解

1. vector向量:相当于一个数组      连续存储结构,每个元素在内存上是连续的;支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;相当于一个数组,但是与...
  • Ants_2
  • Ants_2
  • 2017年07月13日 11:32
  • 123

C++ Primer 学习笔记——第3章:标准库类型 vector

第3章:标准库类型 vector
  • eminia
  • eminia
  • 2011年07月30日 23:31
  • 623
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ Primer学习笔记(11)——(转)c++ 标准库的各种容器(vector,deque,map,set,unordered_map,unordered_set,list)的性能考虑
举报原因:
原因补充:

(最多只允许输入30个字)