flat_set¶
template<
class Key,
class Compare = std::less<Key>,
class KeyContainer = std::vector<Key>
> class flat_set;
类模板 flat_set
表现为对作为 KeyContainer
类型的对象而传递的底层有序容器的包装器。
1 flat_set 和 set 区别¶
set
和 std::flat_set
都使用有序的平衡二叉搜索树,但 flat_set
底层数据存在 KeyContainer 指定的容器里面,默认是 vector。
优点:
1. 迭代更快。
2. 查询更快。同样是log2(n)
,但常数更小。
缺点:
1. 插入、删除慢。插入和删除都会造成旧迭代器的失效,因为不论是插入还是删除,都会伴随着大部分元素的移动,保存的旧迭代器很难不失效
std::flat_set
是 C++23 引入的一个新容器,位于 <flat_set>
头文件中。它与传统的 std::set
类似,都是有序关联容器,但底层实现和性能特性有显著区别。以下是关键差异和适用场景:
2 底层数据结构¶
容器 | 底层结构 | 内存布局 |
---|---|---|
std::set |
红黑树(平衡二叉搜索树) | 节点分散分配(指针链接) |
std::flat_set |
动态数组(如 std::vector ) |
连续内存存储 |
std::set
:每个元素独立分配节点,通过指针连接,适合频繁插入/删除。std::flat_set
:元素存储在连续内存中(类似std::vector
),缓存友好,但插入/删除成本较高。
3 性能特点¶
操作 | std::set |
std::flat_set |
---|---|---|
查找 (find ) |
O (log n) | O (log n)(二分查找,更快因缓存局部性) |
插入/删除 | O (log n) | O (n)(需移动后续元素) |
遍历 | 较慢(指针跳转) | 极快(连续内存,CPU 缓存命中率高) |
内存占用 | 较高(每个节点含指针开销) | 更低(无指针开销,仅存储元素) |
4 使用场景¶
4.1 适合 std::flat_set
的情况:¶
- 频繁查找/遍历,极少修改
- 例如:配置表、只读或初始化后固定的数据集。
- 需要极致缓存友好性
- 对性能敏感的场景(如游戏、高频交易)。
- 内存受限环境
- 减少内存碎片和开销(嵌入式系统)。
4.2 适合 std::set
的情况:¶
- 频繁插入/删除
- 如动态更新的实时数据集合。
- 元素较大或不可移动
std::flat_set
的插入/删除会导致元素移动。
5 代码示例¶
5.1 基本用法(与 std::set
类似)¶
#include <flat_set>
#include <iostream>
int main() {
std::flat_set<int> fs = {3, 1, 4, 1, 5}; // 自动排序去重
fs.insert(2); // 插入(可能触发元素移动)
for (int x : fs) { // 遍历速度极快
std::cout << x << " "; // 输出: 1 2 3 4 5
}
auto it = fs.find(4); // 二分查找
if (it != fs.end()) {
std::cout << "\nFound: " << *it;
}
}
5.2 与 std::set
的对比¶
#include <set>
#include <flat_set>
#include <vector>
void compare() {
std::set<int> s = {3, 1, 4};
std::flat_set<int> fs = {3, 1, 4};
// 插入性能差异
s.insert(2); // O(log n),无数据移动
fs.insert(2); // O(n),可能需要移动元素
// 内存占用对比
std::vector<int> v = {1, 2, 3};
std::cout << sizeof(fs) << "\n"; // 类似 vector(通常 24 字节)
std::cout << sizeof(s) << "\n"; // 包含树结构开销(通常 48 字节)
}
6 注意事项¶
- 插入/删除成本:
std::flat_set
的修改操作需要移动元素,可能触发内存重新分配。 - 迭代器稳定性:
修改操作会使所有迭代器、指针、引用失效(类似std::vector
)。 - 自定义排序:
需在构造函数中指定比较器(与std::set
相同):auto cmp = [](int a, int b) { return a > b; }; std::flat_set<int, decltype(cmp)> fs(cmp);
7 总结¶
- 选择
std::flat_set
:优先考虑查找/遍历性能、内存紧凑性,且数据修改较少时。 - 选择
std::set
:需要频繁插入/删除或元素稳定性时。
std::flat_set
是 C++ 对性能优化需求的响应,通过牺牲修改效率换取查找和内存优势。