一、概述
定义:容器是用来存储和组织数据的类模板
总结:容器-顾名思义就是拿来装东西的。你可以装标准的基本数据类型,如int,double,string等、也可以装复杂的自定义类型,例如class 定义的类的对象,对象指针等复合类型的数据。
C++ stl标准库提供了多种容器,每种容器都有特定的用途和性能特点。以下是 C++ 标准库中一些常见的容器:
二、容器分类
(1)顺序容器
名称 | 说明 | 头文件 |
std::vector | 动态数组,支持快速随机访问和尾部插入/删除 | <vector> |
std::list | 双向链表,支持快速插入/删除操作 | <list> |
std::deque | 双端队列,支持在两端进行快速插入/删除操作 | <deque> |
std::array | 固定大小的数组,提供与 C 风格数组类似的接口,但更安全和易用 | <array> |
(2)关联容器
名称 | 说明 | 头文件 |
std::map | 键-值对的关联容器,基于红黑树实现,支持按键进行快速查找 | <map> |
std::multimap | 允许重复键的键-值对关联容器。 | <map> |
std::set | 唯一键的集合,基于红黑树实现,支持按值进行快速查找 | <set> |
std::multiset | 允许重复键的集合容器 | <set> |
(3)无序容器
名称 | 说明 | 头文件 |
std::unordered_map | 使用哈希表实现的键-值对关联容器,支持快速的插入/删除/查找操作 | <unordered_map> |
std::unordered_multimap | 使用哈希表实现的允许重复键的键-值对关联容器 | <unordered_map> |
std::unordered_set | 使用哈希表实现的唯一键集合,支持快速的插入/删除/查操作 | <unordered_set> |
std::unordered_multiset | 使用哈希表实现的允许重复键的集合容器 | <unordered_set> |
(4)其他容器
名称 | 说明 | 头文件 |
std::stack | 栈,基于其他顺序容器实现的后进先出数据结构 | <stack> |
std::queue | 队列,基于其他顺序容器实现的先进先出数据结构 | <queue> |
std::priority_queue | 优先队列,基于堆实现的数据结构,支持高效的最大/最小值检索 | <queue> |
std::bitset | 固定大小的位集容器,可以高效地存储和操作位数据 | <bitset> |
std::tuple | 用于存储多个不同类型的值,类似于结构体,但更通用和灵活 | <tuple> |
std::any | 可以存储任意类型值的容器,类似于动态类型语言中的变量 | <any> |
std::optional | 表示一个可选的值,可以避免使用空指针或特殊值来表示缺失值 | <optional> |
三、如何选择适合的容器?
3.1 std::vector
- 应用场景:当需要一个可变大小的数组,并且需要在末尾进行快速插入和删除操作时,
std::vector
是一个不错的选择。此外,如果需要通过索引进行快速随机访问,也可以选择std::vector
。 - 注意事项:在中间插入或删除元素时可能会比较慢,因为需要移动后续元素。
3.2 std::list
- 应用场景:当需要在任意位置进行快速插入和删除操作时,
std::list
是一个不错的选择。它是一个双向链表,可以高效地进行插入和删除操作。 - 注意事项:随机访问性能较差,因此不适合需要频繁随机访问元素的情况。
3.3 std::deque
- 应用场景:当需要在两端进行快速插入和删除操作时,
std::deque
是一个不错的选择。它可以看作是std::vector
的泛化版本,支持在两端进行高效操作。 - 注意事项:在中间插入或删除元素时性能可能比较低。
3.4 std::array
- 应用场景:当需要一个固定大小的数组,并且希望拥有数组的性能和语法上的方便时,
std::array
是一个不错的选择。 - 注意事项:大小固定,不能动态扩展。
3.5 std::map
- 应用场景:适合需要键-值对映射关系,并且需要基于键进行快速查找的场景。适合需要唯一键和对数级别查找性能的情况。
- 注意事项:插入和查找的时间复杂度为 O(log n),相较于数组或链表,其常数项略大,因此在小规模数据或性能要求特别高的情况下,可能不是最佳选择。
3.6 std::multimap
- 应用场景:适合需要键-值对映射关系,并且允许重复键的情况。
std::multimap
允许多个元素拥有相同的键,因此适合这种需求的场景。 - 注意事项:通过键进行查找时会返回一个范围,因为可能有多个元素拥有相同的键。
3.7 std::set
- 应用场景:适合需要一个有序、不重复元素集合的情况。
std::set
中的元素是按照严格弱序排列的,且集合中不允许重复元素。 - 注意事项:插入和查找操作的时间复杂度为O(log n),具有较高的性能。
3.8 std::multiset
- 应用场景:适合需要一个有序元素集合,并且允许重复元素存在的情况。
std::multiset
允许集合中存在相同的元素。 - 注意事项:与
std::set
类似,但允许重复元素存在。
3.9 std::unordered_map
- 应用场景:适合需要键-值对映射关系,并且对于查找操作有较高性能要求的情况。
std::unordered_map
基于哈希表实现,具有常数时间复杂度的查找操作。 - 注意事项:相较于
std::map
,哈希冲突可能会影响性能,同时不保证元素的顺序。
3.10 std::unordered_multimap
- 应用场景:适合需要键-值对映射关系,并且允许重复键存在,同时对于查找操作有较高性能要求的情况。
std::unordered_multimap
基于哈希表实现,允许重复键存在。 - 注意事项:同样可能存在哈希冲突和无序性的问题。
3.11 std::unordered_set
- 应用场景:适合需要一个无序、不重复元素集合,并且对于查找操作有较高性能要求的情况。
std::unordered_set
基于哈希表实现,具有常数时间复杂度的查找操作。 - 注意事项:不保证元素的顺序,可能存在哈希冲突。
3.12 std::unordered_multiset
- 应用场景:适合需要一个无序元素集合,并且允许重复元素存在,同时对于查找操作有较高性能要求的情况。
std::unordered_multiset
基于哈希表实现,允许重复元素存在。 - 注意事项:同样可能存在哈希冲突和无序性的问题。
3.13 std::stack
- 应用场景:适合需要“后进先出”(LIFO)数据结构的情况。
std::stack
基于其他容器(默认为std::deque
)实现,提供了栈的基本操作。
3.14 std::queue
- 应用场景:适合需要“先进先出”(FIFO)数据结构的情况。
std::queue
基于其他容器(默认为std::deque
)实现,提供了队列的基本操作。
3.15 std::priority_queue
- 应用场景:适合需要按照一定顺序动态获取最大(或最小)值的情况。
std::priority_queue
通常基于堆实现,可以高效地获取当前堆中的最大(或最小)值。
3.16 std::bitset
- 应用场景:适合需要高效存储位信息的情况。
std::bitset
是一个固定大小的位集合,支持进行位操作,并且在存储和查询位信息方面具有较高的性能。
3.17 std::tuple
- 应用场景:适合需要将多个值作为一个整体处理的情况。
std::tuple
可以看作是一个通用的轻量级结构体,用于将若干个值组合成一个整体。
3.18 std::any
- 应用场景:适合需要在运行时存储和访问任意类型的值的情况。
std::any
可以存储任何类型的值,提供了一种通用的值容器。
3.19 std::optional
- 应用场景:适合表示可能不存在值的情况。
std::optional
可以包含一个值或者为空,用于代表一个可能缺失的值。
四、关键容器的对比
以上列出了所介绍的容器的应用场景和注意事项,有的容器,对于应用场景不同的容器,选择起来很容易,对于应用场景相同的场景,选择起来需要考虑性能,以下对几个比较特殊的容器进行详细对比:
4.1 std::map
和 std::unordered_map
主要区别在于底层实现和查找性能。
std::map:
std::map
是基于红黑树实现的关联容器,它将键-值对按照键的大小进行排序存储。这意味着在使用std::map
时,元素是按照键值的大小有序排列的。- 查找、插入和删除操作的平均时间复杂度为 O(log n),具有较好的有序性能。
std::unordered_map:
std::unordered_map
是基于哈希表实现的关联容器,它使用键的哈希值来组织和快速查找元素。这意味着在使用std::unordered_map
时,元素是无序存储的。- 查找、插入和删除操作的平均时间复杂度为 O(1),具有非常高的查找性能,但不保证元素的顺序。
总结:
std::map
保持元素的有序性,适合需要有序访问的场景,而std::unordered_map
不保证元素的顺序,但提供了更快的查找性能。- 在对有序性要求不高,但对查找性能有较高要求的情况下,可以选择
std::unordered_map
来获得更好的性能。
4.2 std::multimap与std::unordered_multimap
std::multimap:
std::multimap
是基于红黑树实现的关联容器,允许多个元素拥有相同的键。它按照键的大小进行排序存储,因此在使用std::multimap
时,元素是按照键值的大小有序排列的。- 查找、插入和删除操作的平均时间复杂度为 O(log n),并且保持元素的有序性。
std::unordered_multimap:
std::unordered_multimap
是基于哈希表实现的关联容器,同样允许多个元素拥有相同的键。它使用键的哈希值来组织和快速查找元素,因此在使用std::unordered_multimap
时,元素是无序存储的。- 查找、插入和删除操作的平均时间复杂度为 O(1),具有非常高的查找性能,但不保证元素的顺序。
4.3 std::set 与 std::unordered_set
std::set:
std::set
是基于红黑树实现的集合容器,用于存储不重复的元素,并按照元素的大小进行排序存储。这意味着在使用std::set
时,元素是按照大小有序排列的。- 查找、插入和删除操作的平均时间复杂度为 O(log n),并且保持元素的有序性。
std::unordered_set:
std::unordered_set
是基于哈希表实现的集合容器,用于存储不重复的元素,但不保证元素的顺序。它使用元素的哈希值来组织和快速查找元素,因此在使用std::unordered_set
时,元素是无序存储的。- 查找、插入和删除操作的平均时间复杂度为 O(1),具有非常高的查找性能,但不保证元素的顺序。
因此,主要区别可以总结如下:
std::set
保持元素的有序性,适合需要有序访问的场景,而std::unordered_set
不保证元素的顺序,但提供了更快的查找性能。- 在对有序性要求不高,但对查找性能有较高要求的情况下,可以选择
std::unordered_set
来获得更好的性能
4.4 总结
对于map,set,multimap,multiset,当对顺序有要求时候,选择这些,对顺序无要求时候,可以选择带unordered_前缀的容器,以提高性能。
五、总结
本文介绍了C++11 stl常见的容易得分类,应用场景以及性能对比,仔细阅读本文,你应该可以选择出合适的容易用于你的项目中。
后续章节将详细介绍本文所提到的所有的容器的使用方法,代码示例,将详细介绍每一种容器的:增、删、改、查、遍历。