当前位置: 首页 > news >正文

flat_map, flat_set, flat_multimap, flat_multimap

这些是C++23引入的新容器类型,属于"扁平化关联容器"(flat associative containers)家族。它们提供了与标准关联容器(map, set等)类似的接口,但使用不同的底层实现,从而在某些场景下提供更好的性能。

基本概念

这些扁平化容器与标准关联容器的主要区别在于:

  1. 底层实现:使用排序的连续存储(通常是vector或类似结构)而非树结构
  2. 内存布局:元素在内存中是连续存储的。
  3. 性能特点
    • 查找速度比标准容器更快(两者都是二分查找,但是flat_map/set等内存连续,是缓存友好的,提高缓存命中率,总体速度比map/set等快)。
    • 插入和删除通常较慢(需要对数组中的元素进行整体的移动,尤其不支持移动构造时)
    • 迭代速度更快(由于内存局部性)
    • 内存占用更小(相对于平衡二叉树,减少了节点指针的维护,从而节省了内存空间)

各容器详解

1. flat_map

类似std::map,但使用扁平化存储的键值对容器。

  • 查找速度比标准关联容器std::map更快(二者使用二分查找时间复杂度为O(log n),但是flat_map底层使用数组存储,在缓存友好性方面优于树结构,缓存命中率通常要高)。
  • 迭代速度比标准关联容器std::map快得多 。
  • 使用随机访问迭代器而不是双向迭代器。
  • 每个元素的内存消耗更少。
  • 改进的缓存性能(数据存储在连续内存中,提高缓存命中率)。
  • 迭代器不稳定(插入和删除元素时迭代器会失效)。
  • 无法存储不可复制和不可移动的值类型。
  • 异常安全性比标准关联容器弱(在删除和插入时移动值可能会抛出异常)。
  • 插入和删除操作比标准关联容器慢(尤其是对于不可移动类型,插入和删除复杂度 O(N) 甚至高于map的 O(log⁡N))。

flat_map原理

flat_map:底层使用了有序数组,pair<Key, Value>直接作为元素存储在内,查找时通过二分法的方式定位数据,插入和删除时需要移动目标元素后的所有元素。

std::flat_map将所有的Key保存在连续的内存空间中(比如std::vector)。同样的,所有的Value也保存在连续的内存空间中。比如说,我们有三个键值对:(1, 2), (5, 6), (3, 4),那么它们在std::flat_map中的存储如下图所示:

注意,Key序列是有序存储的,并且Key[i]Value[i]构成了一个键值对。

std::flat_map的遍历就是对这两个数组的遍历,由于数据是连续存储的,所以遍历速度会显著快于std::map

如果对std::flat_map进行查找操作,只需要在Key数组上做binary_search即可,最多比较 log2⁡(n) 次。相比之下,std::map的红黑树实现的树高可以达到 2log2⁡(n+1) ,比较次数多于std::flat_map

但是如果要对std::flat_map进行插入/删除操作,需要对数组中的元素进行整体的移动,最坏时间复杂度为 O(n) 。

std::flat_map有5个模板参数,分别表示:

  1. Key的类型
  2. Value的类型
  3. 比较器(默认为std::less<Key>
  4. Key的存储容器类型(默认为std::vector<Key>
  5. Value的存储容器类型(默认为std::vector<T>
#include <flat_map>
#include <iostream>
#include <string>int main() 
{std::flat_map<std::string, int> ages{{"Alice", 30},{"Bob", 25},{"Charlie", 35}};// 插入元素(可能触发排序和移动)ages.insert({"David", 28});// 查找元素if (auto it = ages.find("Alice"); it != ages.end()) {std::cout << "Alice is " << it->second << " years old\n";}// 迭代for (const auto& [name, age] : ages) {std::cout << name << ": " << age << "\n";}
}

2. flat_set

类似std::set,但使用扁平化存储的唯一键集合。

#include <flat_set>
#include <iostream>int main() 
{std::flat_set<int> primes{2, 3, 5, 7, 11};// 插入元素primes.insert(13);// 检查存在性if (primes.contains(7)) {std::cout << "7 is a prime number\n";}// 范围查询auto [begin, end] = primes.equal_range(5);for (auto it = begin; it != end; ++it) {std::cout << *it << " ";}std::cout << "\n";
}

性能考虑

  1. 适合场景

    • 需要频繁查找但较少插入/删除
    • 需要快速迭代
    • 内存受限环境
  2. 不适合场景

    • 需要频繁插入/删除
    • 需要稳定的迭代器(插入/删除会使迭代器失效)
  3. 与标准容器比较

    • 查找:O(log n) - 与标准容器相同,但是缓存友好。
    • 插入:O(n) - 比标准容器的O(log n)慢
    • 内存:通常更节省(没有指针开销)

自定义比较和分配器

与标准容器一样,可以自定义比较函数和分配器:

struct CaseInsensitiveCompare 
{bool operator()(const std::string& a, const std::string& b) const {return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),[](char c1, char c2) { return tolower(c1) < tolower(c2); });}
};std::flat_set<std::string, CaseInsensitiveCompare> caseInsensitiveSet;

性能:xhttps://zhuanlan.zhihu.com/p/661418250

http://www.xdnf.cn/news/508177.html

相关文章:

  • 深入理解位图(Bit - set):概念、实现与应用
  • python中http.cookiejar和http.cookie的区别
  • 深入解析Spring Boot与Kafka集成:构建高性能消息驱动应用
  • 【技海登峰】Kafka漫谈系列(十一)SpringBoot整合Kafka之消费者Consumer
  • 【云原生架构反模式】常见误区与解决方案
  • WPS多级标题编号以及样式控制
  • ES(ES2023/ES14)最新更新内容,及如何减少内耗
  • 大模型微调:从基础模型到专用模型的演进之路
  • IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink
  • 为新装的Linux系统配置国内yum源(阿里源)
  • 19. 结合Selenium和YAML对页面实例化PO对象改造
  • 大数据场景下数据导出的架构演进与EasyExcel实战方案
  • 理想AI Talk第二季-重点信息总结
  • 【架构美学】Java 访问者模式:解构数据与操作的双重分发哲学
  • 基于单片机路灯自动控制仪仿真设计
  • 包装设备跨系统兼容:Profinet转Modbus TCP的热收缩包装机改造方案
  • 出现 Uncaught ReferenceError: process is not defined 错误
  • 【NLP 75、如何通过API调用智谱大模型】
  • Spring Web MVC————入门(3)
  • ngx_http_rewrite_module 技术指南
  • 2025年、2024年最新版IntelliJ IDEA下载安装过程(含Java环境搭建+Maven下载及配置)
  • 操作系统之EXT文件系统
  • windows笔记本连接RKNN3588网络配置解析
  • Go 与 Gin 搭建简易 Postman:实现基础 HTTP 拨测的详细指南
  • golang选项设计模式
  • Linux518 YUM源仓库回顾(需查)ssh 服务配置回顾 特定任务配置回顾
  • 51单片机,两路倒计时,LCD1602 ,Proteus仿真
  • 逻辑与非逻辑的弥聚
  • C++笔试题(金山科技新未来训练营):
  • 基于simulink搭建的模块化多电平MMC仿真模型