C++提高编程(三)—— STL常用容器(11) :容器总结

C++系列内容的学习目录 → \rightarrow C++学习系列内容汇总。

  • 1. 顺序容器
  • 2. 容器适配器
  • 3. 关联容器

1. 顺序容器

  一个容器就是一些特定类型对象的集合。顺序容器(sequential container)为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。

  下表列出了标准库中的顺序容器,所有容器都提供了快速顺序访问元素的能力,但是这些容器在两个方面都有不同的性能折中。第一个方面是,向容器中添加或从容器中删除元素的代价;第二个方面是,非顺序访问容器中元素的代价。

容器类型 简介 博客链接
vector 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 C++提高编程(三)—— STL常用容器(2) :vector容器
deque 双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。 C++提高编程(三)—— STL常用容器(3) :deque容器
list 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。 C++提高编程(三)—— STL常用容器(7) :list容器
forward_list 单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。
array 固定大小数组。支持快速随机访问。不能添加或删除元素。
string 与vector相似的容器,但专门用于保存字符。随机访问快。在尾部位置插入/删除操作速度都很快。 C++提高编程(三)—— STL常用容器(1) :string容器

  除了固定大小的array外,其他容器都提供高效、灵活的内存管理。我们可以添加和删除元素,扩张和收缩容器的大小。容器保存元素的策略对容器操作的效率有着固有的、甚至是重大的影响。在某些情况下,存储策略还会影响特定容器是否支持特定操作。

  例如,string和vector将元素保存在连续的内存空间中。由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是,在这两种容器的中间位置添加或者删除元素就会非常耗时。因为在一次插入或删除操作后,需要移动插入或删除位置之后的所有元素来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。

  list和forward_list两个容器的设计目的是令容器任何位置的添加或删除操作都很快速。作为代价,这两个容器不支持元素的随机访问。为了访问一个元素,我们只能遍历整个容器。而且,与vector、deque和array相比,这两个容器的额外内存开销也很大。

  deque是一个更为复杂的数据结构。与string和vector类似,deque支持快速的随机访问。与string和vector一样,在deque的中间位置添加或删除元素的代价(可能)很高。但是,在deque的两端添加或删除元素都是很快的,与list或forward_list添加删除元素的速度相当。

  forward_list和array是新C++标准增加的类型。与内置数组相比,array是一种更安全、更容易使用的数组类型。与内置数组类似,array对象的大小是固定的。因此,array不支持添加和删除元素以及改变容器大小的操作。forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。因此,forward_list没有size操作,因为保存或计算其大小就会比手写链表多出额外的开销。对其他容器而言,size保证是一个快速的常量时间的操作。

  确定使用哪种顺序容器的基本原则

  • 通常,使用vector是最好的选择,除非有很好的理由选择其他容器;
  • 如果程序有很多小的元素,而且空间的额外开销很重要,则不要使用list或forward_list;
  • 如果程序要求随机访问元素,应使用vector或deque;
  • 如果程序要求在容器的中间插入或删除元素,应使用list或forward_list;
  • 如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque;
  • 如果程序只有在读取输入时才需要在容器中间位置插入元素,随后需要随机访问元素,则
      首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容易地向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素;
      如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。

  如果程序既需要随机访问元素,又需要在容器中间位置插入元素,那该怎么办?答案取决于在list或forward_list中访问元素与vector或deque中插入/删除元素的相对性能。一般来说,应用中占主导地位的操作(执行的访问操作更多还是插入删除更多)决定了容器类型的选择,在此情况下,对两种容器分别测试应用的性能可能就是必要的了。

  如果不确定应该使用哪种容器,那么可以在程序中只使用vector和list公共的操作:使用选代器,不使用下标操作,避免随机访问。这样,在必要时选择使用vector或list都很方便。

2. 容器适配器

  除了顺序容器之外,标准库还定义了三个顺序容器适器:stack(栈)、queue(队列)和priority_queue(优先队列)。适配器(adaptor)是标准库中的一个通用概念,容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。例如,stack适配器接受一个顺序容器(除array或forward_list外),并使其操作起来像一个stack一样。

  下表列出了一些容器适配器。

容器适配器类型 简介 博客链接
stack 一种先进后出(FILO)的数据结构,只有一个出口。 C++提高编程(三)—— STL常用容器(5) :stack容器
queue 一种先进先出(FIFO)的数据结构,有两个出口。 C++提高编程(三)—— STL常用容器(6) :queue容器
 priority_queue  每次从队列中取出具有最高优先级的元素。

  默认情况下,stack和queue是基于deque实现的,priority_queue是基于vector之上实现的。

3. 关联容器

  关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置顺序来保存和访问的。

  关联容器支持高效的关键字查找和访问,两个主要的关联容器(associative-container)类型是map和set,map中的元素是一些关键字 - 值(key-value)对:关键字起到索引的作用,值则表示与索引相关联的数据,set中每个元素只包含一个关键字。set支持高效的关键字查询操作——检查一个给定关键字是否在set中,例如,在某些文本处理过程中,可以用一个set来保存想要忽略的单词。字典则是一个很好的使用map的例子:可以将单词作为关键字,将单词释义作为值。

  下表列出了一些关联容器。

容器类型 简介 博客链接
set 关键字即值,即只保存关键字的容器 C++提高编程(三)—— STL常用容器(8) :set / multiset容器
map 关联数组;保存关键字 - 值对 C++提高编程(三)—— STL常用容器(9) :map / multimap容器
multiset 关键字可重复出现的set C++提高编程(三)—— STL常用容器(8) :set / multiset容器
multimap 关键字可重复出现的map C++提高编程(三)—— STL常用容器(9) :map / multimap容器

你可能感兴趣的:(C++,容器,c++,编程语言)