C++ STL multimap:一对多关联容器的原理、操作与实战应用
1. 从“键值对”到“一对多”为什么需要multimap在C的STL标准模板库里map和multimap这对兄弟常常让初学者感到困惑。我们都很熟悉std::map它就像一个高效的字典每个“键”key都独一无二指向一个特定的“值”value。你输入一个名字就能立刻找到对应的电话号码这种一对一的映射关系清晰、直接是处理大量关联数据的利器。但现实世界的数据关系远比一对一复杂。想象一下你要管理一个部门的员工信息以“部门名称”作为键员工对象作为值。在map的世界里一个部门只能对应一个员工这显然不合理。你需要的是一个能容纳多个值的“键”。再比如处理日志文件以“时间戳精确到秒”为键记录这一秒内发生的所有事件消息或者构建一个单词的倒排索引每个单词键对应它在文档中出现过的所有位置值列表。在这些场景下键与值是一对多的关系。这就是std::multimap存在的意义。它是关联容器的一种允许容器中存在多个具有相同键的键值对。你可以把它理解为一个自动排序的“多值字典”。它底层通常基于红黑树实现这意味着它的元素总是按键的升序排列默认情况下并且插入、删除、查找操作都能保持对数时间复杂度O(log n)在需要有序遍历或范围查询的场景下非常高效。很多从map转过来的朋友第一次用multimap时最容易踩的坑就是试图用operator[]去访问元素。在map里myMap[“key”]能轻松拿到或插入值但在multimap里这个操作符直接被禁用了。为什么因为对于一个键可能对应多个值的情况operator[]的语义变得模糊——它到底该返回哪一个值呢这个设计决策迫使我们必须换一种更精确的方式来与multimap交互这也是理解其用法的第一个关键点。2. multimap的核心操作插入、查找与遍历与map相比multimap的接口设计强调了“多值”的特性。我们不再能简单地通过键来获取单一值而是需要处理一个值的集合。2.1 元素的插入插入操作和map类似使用insert成员函数。但由于允许多个相同键插入总是会成功除非发生异常。#include iostream #include map #include string int main() { std::multimapstd::string, int scoreMap; // 插入多个相同键的值 scoreMap.insert({Alice, 85}); scoreMap.insert({Bob, 90}); scoreMap.insert({Alice, 92}); // 第二个Alice scoreMap.insert({Alice, 88}); // 第三个Alice // 也可以使用 make_pair (C11 前) 或 emplace (C11 后) scoreMap.emplace(Bob, 95); }这里“Alice”这个键关联了三个分数859288。需要注意的是multimap会保持所有元素的排序状态。对于相同键的多个值它们会按照插入的顺序依次排列吗实际上C标准只保证相同键的元素会相邻存储但并没有规定它们之间的相对顺序。不过在常见的实现中如GCC、Clang的libstdc MSVC的STL这些相同键的值通常会按照插入的先后顺序排列但这不是可移植的保证。如果你的业务逻辑依赖相同键值对的顺序更安全的做法是使用std::mapstd::string, std::vectorint将顺序的控制权完全掌握在自己手里。2.2 元素的查找与访问这是multimap与map用法差异最大的地方。由于一个键对应多个值我们通常使用一对迭代器来获取这个范围。find与count的组合拳find(key)会返回指向第一个匹配键的元素的迭代器。count(key)则返回该键对应的元素数量。// 查找键为Alice的所有记录 std::string keyToFind Alice; int num scoreMap.count(keyToFind); // 返回 3 if (num 0) { auto it scoreMap.find(keyToFind); // 找到第一个Alice for (int i 0; i num; i, it) { std::cout it-first : it-second std::endl; } }这种方法简单但需要注意在循环中我们手动递增迭代器num次这依赖于一个假设——所有相同键的元素都紧挨着。在multimap中这个假设是成立的。更优雅的方式equal_rangeequal_range(key)是处理multimap查找的“标准答案”。它返回一个pair其中first是指向第一个键等于key的元素的迭代器second是指向最后一个键等于key的元素之后位置的迭代器。这个区间就是所有该键对应的值用起来非常符合STL的区间习惯。auto range scoreMap.equal_range(Alice); for (auto it range.first; it ! range.second; it) { std::cout it-first - it-second std::endl; } // 输出 // Alice - 85 // Alice - 92 // Alice - 88使用equal_range的代码更清晰且不依赖于count直接利用迭代器区间是推荐的做法。lower_bound与upper_bound这两个函数也常用于查找范围。lower_bound(key)返回指向第一个不小于key的元素的迭代器即第一个键等于key或大于key的位置。upper_bound(key)返回指向第一个大于key的元素的迭代器。因此[lower_bound(key), upper_bound(key))这个左闭右开区间同样包含了所有键等于key的元素。auto low scoreMap.lower_bound(Alice); auto up scoreMap.upper_bound(Alice); for (auto it low; it ! up; it) { // 处理每一个Alice }在multimap中equal_range(key)的实现通常就等价于std::make_pair(lower_bound(key), upper_bound(key))。你可以根据代码语境选择更贴切的一个。2.3 元素的遍历与删除遍历整个multimap和遍历其他容器一样使用迭代器即可。因为元素是排序的所以遍历顺序就是键的升序。for (const auto kv : scoreMap) { std::cout kv.first : kv.second std::endl; } // 输出可能是 // Alice: 85 // Alice: 92 // Alice: 88 // Bob: 90 // Bob: 95删除操作有几种形式erase(iterator pos)删除迭代器pos指向的元素。erase(key_type key)删除所有键等于key的元素并返回被删除的元素数量。这是与map的erase(key)行为不同的地方map只删除一个如果存在而multimap会删除全部。erase(iterator first, iterator last)删除迭代器区间[first, last)内的所有元素。如果你想删除特定键的某一个特定值比如只删除Alice的92分你需要先定位到那个具体的元素迭代器然后删除。auto range scoreMap.equal_range(Alice); for (auto it range.first; it ! range.second; ) { if (it-second 92) { it scoreMap.erase(it); // erase 返回被删除元素之后位置的迭代器 } else { it; } }这里有一个关键细节在遍历中删除元素时erase(it)会使it失效。但幸运的是erase成员函数会返回下一个有效迭代器所以我们采用it container.erase(it)的模式来安全地继续循环。3. 底层实现与性能考量红黑树的利与弊std::multimap的默认实现底层是一棵红黑树一种自平衡的二叉查找树。这决定了它的特性有序性元素始终按照键的顺序默认std::less即升序排列。这使得范围查询如lower_bound/upper_bound和有序遍历非常高效。稳定的对数时间复杂度插入(insert)、删除(erase)、查找(find)操作的平均和最坏情况时间复杂度都是O(log n)其中n是容器中元素的数量。性能可预测不会像哈希表那样在极端情况下退化。内存开销每个元素都是一个独立的节点除了存储键值对还需要存储额外的指针指向父节点、左右子节点和颜色信息内存开销比std::vector或std::unordered_multimap要大。与std::map的唯一区别就在于multimap的树节点允许键重复。在插入一个新节点时如果键已存在树会将其插入到相同键的节点序列中的适当位置通常遵循特定的内部规则如维护插入顺序。与unordered_multimap的对比std::unordered_multimap是基于哈希表实现的关联容器。它的核心优势是平均情况下的O(1)时间复杂度访问但最坏情况可能退化到O(n)。它不保证元素的任何顺序。 如何选择选用multimap当你需要元素按键排序或者需要频繁进行范围查询“找出所有键在A和B之间的元素”。例如处理时间序列数据你需要按时间顺序处理事件。选用unordered_multimap当顺序无关紧要且你追求极致的平均访问速度同时哈希函数对你的键类型效果很好时。例如实现一个快速的单词计数器键是单词值是计数虽然这种情况用unordered_mapstring, int更常见。一个性能陷阱大量重复键下的查找假设一个multimap中有100万个元素其中键“X”对应了50万个值。当你调用equal_range(“X”)时虽然查找第一个“X”是O(log n)但遍历这50万个值的过程是O(k)k是重复数量。如果这是你的核心操作且k很大线性遍历可能会成为瓶颈。此时或许std::mapKey, std::vectorValue是更好的选择因为你可以直接通过map[key]拿到整个vector访问是O(1)并且你对vector内的顺序有完全的控制权。代价是失去了整个结构的全局有序性。4. 实战场景与设计模式何时该用multimap理解了基本操作和原理我们来看看multimap在哪些实际场景中能真正发光发热。场景一事件调度器Event Scheduler假设你在写一个游戏或模拟程序需要处理在特定游戏时间点触发的多个事件。std::multimapdouble, std::functionvoid() eventQueue; // 键: 触发时间, 值: 事件函数 // 订阅事件 eventQueue.emplace(10.5, [](){ std::cout Spawn enemy at 10.5s\n; }); eventQueue.emplace(5.0, [](){ std::cout Play sound at 5.0s\n; }); eventQueue.emplace(10.5, [](){ std::cout Update score at 10.5s\n; }); // 主循环中处理事件 double currentTime 0.0; while (!eventQueue.empty() eventQueue.begin()-first currentTime) { auto range eventQueue.equal_range(eventQueue.begin()-first); for (auto it range.first; it ! range.second; ) { it-second(); // 执行事件 it eventQueue.erase(it); // 执行后移除 } // 更新currentTime... }这里multimap保证了事件按时间顺序处理并且能优雅地处理同一时刻的多个事件。场景二多值配置读取从配置文件中读取设置一个配置项可能对应多个值例如服务器监听的多个端口用户拥有的多个角色。// 假设从文件解析后得到 std::multimapstd::string, std::string config; config.emplace(server.port, 8080); config.emplace(server.port, 8081); config.emplace(user.role, admin); config.emplace(user.role, editor); // 读取所有端口 auto ports config.equal_range(server.port); std::vectorint portList; for (auto it ports.first; it ! ports.second; it) { portList.push_back(std::stoi(it-second)); }场景三构建反向索引Inverted Index这是搜索引擎的核心数据结构之一。对于一组文档我们建立“单词”到“出现该单词的文档ID列表”的映射。std::multimapstd::string, int invertedIndex; // 键: 单词, 值: 文档ID // 假设文档1包含单词 apple, banana invertedIndex.emplace(apple, 1); invertedIndex.emplace(banana, 1); // 文档2包含单词 apple invertedIndex.emplace(apple, 2); // 搜索包含apple的文档 auto docs invertedIndex.equal_range(apple); std::setint uniqueDocIds; // 用set去重因为同一个单词在同一文档可能出现多次 for (auto it docs.first; it ! docs.second; it) { uniqueDocIds.insert(it-second); } // uniqueDocIds 包含 {1, 2}在这个场景中multimap自然地存储了单词和文档ID的对应关系。但需要注意的是对于大规模索引unordered_multimap或mapstring, vectorint可能因为更好的局部性而具有更高的效率。设计模式思考multimapvsmap of container这是使用multimap时最常遇到的设计抉择。是选择std::multimapKey, Value还是std::mapKey, std::vectorValuestd::multimapKey, Value优点结构简单STL直接支持范围查询和有序遍历内存分配是分散的每个节点独立插入删除单个元素可能更高效尤其是中间插入。缺点相同键的值遍历是线性时间且值存储分散缓存不友好。无法直接获取某个键的所有值的集合需要遍历。std::mapKey, std::vectorValue优点可以直接通过map[key]访问整个值列表O(log n)查找O(1)访问数据局部性好对值的批量操作方便例如对整个vector排序。对“获取某个键的所有值”这个操作更直观高效。缺点结构稍复杂。插入第一个值时需要构造vector。如果频繁在vector中间插入删除成本可能较高。选择的关键在于你的主要访问模式。如果你最常做的操作是“针对某个键处理它的所有值”那么map of vector通常更优。如果你的操作是“在整个集合中按序遍历或进行键的范围查询”并且需要频繁插入删除单个键值对那么multimap可能更合适。5. 进阶技巧与常见“坑点”掌握了基础我们来看看一些能提升代码质量和效率的进阶用法以及那些容易让人栽跟头的地方。自定义比较函数和map一样multimap的模板第三个参数是比较器Comparator。默认是std::lessKey即升序。你可以自定义排序规则。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 ca, char cb) { return std::tolower(ca) std::tolower(cb); } ); } }; std::multimapstd::string, int, CaseInsensitiveCompare myMap; myMap.emplace(Apple, 1); myMap.emplace(banana, 2); myMap.emplace(apple, 3); // 此时Apple和apple被视为相同的键注意自定义比较函数必须实现严格弱序否则会导致未定义行为。“坑点”一误用find进行存在性判断在map中我们常用if (myMap.find(key) ! myMap.end())来判断键是否存在。在multimap中这仍然有效但它只告诉你“至少存在一个”该键的元素。如果你需要知道“是否存在且唯一”则需要结合count。std::multimapint, int mm; mm.emplace(1, 100); mm.emplace(1, 200); if (mm.find(1) ! mm.end()) { // 会进入这里因为键1存在 } if (mm.count(1) 1) { // 不会进入这里因为键1有2个值 }“坑点”二迭代器失效的微妙之处multimap的迭代器在插入操作后通常不会失效除非重平衡导致节点移动但标准规定插入不会使任何迭代器失效。但是删除操作会使指向被删除元素的迭代器失效。正如之前删除示例中提到的必须使用erase的返回值来更新迭代器。// 错误示例遍历并删除满足条件的元素 for (auto it mm.begin(); it ! mm.end(); it) { if (someCondition(*it)) { mm.erase(it); // it 在此处失效 // 下一轮循环 it 行为未定义 } } // 正确示例 (C11前) for (auto it mm.begin(); it ! mm.end(); /* 不在这里递增 */) { if (someCondition(*it)) { it mm.erase(it); // erase 返回下一个有效迭代器 } else { it; } } // 正确示例 (C20 起更简洁) std::erase_if(mm, someCondition);“坑点”三性能误区——线性查找新手有时会写出这样的代码std::multimapstd::string, Data bigMap; // ... 填充大量数据 ... // 低效做法在循环内多次调用 equal_range 或 find for (const auto targetKey : keyList) { auto range bigMap.equal_range(targetKey); for (auto it range.first; it ! range.second; it) { process(*it); } } // 如果keyList很大且bigMap也很大这相当于多次O(log n)查找。 // 如果keyList本身无序且需要处理bigMap中大部分数据不如直接遍历bigMap一次。 for (const auto kv : bigMap) { if (shouldProcess(kv.first)) { // shouldProcess判断是否在keyList中 process(kv); } } // 或者如果keyList有序可以考虑使用双指针或同时遍历的方式优化。关键在于理解你的数据规模和访问模式选择最合适的算法。与std::pair和结构化绑定的配合multimap的元素类型是std::pairconst Key, Value。C17的结构化绑定让遍历代码更清晰。std::multimapint, std::string mm; // ... 插入数据 ... for (const auto [key, value] : mm) { // 结构化绑定 std::cout key : value std::endl; } // 在 equal_range 循环中同样适用 auto [begin, end] mm.equal_range(42); // C17 结构化绑定 for (auto it begin; it ! end; it) { const auto [k, v] *it; // 对迭代器解引用也可以绑定 // 使用 k 和 v }std::multimap是一个强大的工具但它不是万能的。它的价值在于对有序的、一对多关系的优雅表达。下次当你面临需要将多个值关联到一个键并且希望它们保持有序时不妨考虑一下它。但在按下CtrlI自动补全选择它之前先花几秒钟想想我真的需要全局有序吗我的主要操作是范围查询还是按键取值数据量有多大重复键多吗想清楚这些问题你就能在multimap、unordered_multimap和map of container之间做出最明智的选择。

相关新闻

信号简介...

信号简介...

#./XXX 前台进程 #./YYY& 后台进程 前台进程能从键盘获取标准输入,后台进程不能 但二者都可以向标准输出上打印jobs查看所有的后台任务fg任务号将特定的进程提到前台ctrlz将进程切换到后台bg任务号让后台进程恢复运行信号 1 kill 2 raisetask_struct内部维…

2026/8/5 10:49:49 阅读更多 →
PostgreSQL跨Schema/Tablespace数据迁移实战指南

PostgreSQL跨Schema/Tablespace数据迁移实战指南

1. PostgreSQL逻辑备份迁移实战:跨Schema/Tablespace的数据导入技巧 作为PostgreSQL数据库管理员,数据迁移是日常工作中最常见的任务之一。最近在将测试环境数据迁移到生产环境时,遇到了需要将数据导入到不同schema和tablespace的需求。经过多…

2026/8/5 10:49:49 阅读更多 →
【爱马仕】Hermes Agent 桌面端落地方案,Windows 可视化部署完整流程

【爱马仕】Hermes Agent 桌面端落地方案,Windows 可视化部署完整流程

Windows 本地部署 Hermes 流程简化,整合包五分钟快速搭建 不少使用者想要体验 Hermes Agent,但在原生部署阶段很容易卡在环境配置环节。 手动安装各类运行依赖、调试系统运行路径、处理命令行报错、应对系统安全拦截、补全缺失文件等一系列操作&#xf…

2026/8/5 10:48:49 阅读更多 →

最新新闻

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否担心QQ空间里那些承载青春记忆的说说会随着时间流逝而消失&…

2026/8/5 11:44:16 阅读更多 →
终极泰拉瑞亚地图编辑器:10分钟从新手变造物主

终极泰拉瑞亚地图编辑器:10分钟从新手变造物主

终极泰拉瑞亚地图编辑器:10分钟从新手变造物主 【免费下载链接】Terraria-Map-Editor TEdit - Terraria Map Editor - TEdit is a stand alone, open source map editor for Terraria. It lets you edit maps just like (almost) paint! It also lets you change wo…

2026/8/5 11:44:16 阅读更多 →
Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化

Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化

Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 你是否在使用Figma时被英文界面困扰?专业术语难懂、菜单…

2026/8/5 11:44:16 阅读更多 →
从功能实现到工程化落地:异步任务与消息队列的实战应用

从功能实现到工程化落地:异步任务与消息队列的实战应用

1. 一个真实的项目复盘:从“功能跑通”到“工程落地”的鸿沟最近和几个刚入行的朋友聊天,发现一个挺普遍的现象:很多人,包括我自己刚工作时,都容易陷入一个误区——把“功能实现”等同于“项目成功”。比如&#xff0c…

2026/8/5 11:44:16 阅读更多 →
如何快速解决常见问题:RVC语音转换WebUI的7个终极技巧

如何快速解决常见问题:RVC语音转换WebUI的7个终极技巧

如何快速解决常见问题&#xff1a;RVC语音转换WebUI的7个终极技巧 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Convers…

2026/8/5 11:44:16 阅读更多 →
CUDA编程入门:从.cu文件结构到并行计算实战

CUDA编程入门:从.cu文件结构到并行计算实战

1. 从“Hello, World!”到并行计算&#xff1a;CUDA编程的初体验如果你是一名C/C开发者&#xff0c;第一次接触CUDA编程&#xff0c;打开一个.cu文件时&#xff0c;可能会觉得既熟悉又陌生。它看起来就像普通的C代码&#xff0c;但里面多了些奇怪的修饰符&#xff0c;比如__glo…

2026/8/5 11:43:15 阅读更多 →

日新闻

Java缓存框架:JetCache

Java缓存框架:JetCache

TOC 一、简介 JetCache 是一个 Java 缓存抽象框架&#xff0c;为不同的缓存解决方案提供了统一的使用方式。 它提供的注解比 Spring Cache 更加强大。 JetCache 的注解支持原生 TTL、两级缓存以及在分布式环境中的自动刷新功能&#xff0c;同时你也可以通过代码直接操作 Cach…

2026/8/5 0:00:43 阅读更多 →
AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

需求&#xff1a;通孔焊盘 十字花&#xff1b;过孔 Via 实心直连&#xff1b;贴片焊盘按需设置 AD 测试版本AD24 很多工程师踩坑&#xff1a;全部统一十字&#xff0c;导致接地过孔阻抗高、大电流发热&#xff01; 一、快捷键打开规则 PCB 界面按下&#xff1a;D R 展开…

2026/8/5 0:00:43 阅读更多 →
AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI生成素描效果 AI生成素描效果是计算机视觉与风格迁移技术融合的典型应用&#xff0c;其核心在于将彩色照片或RGB图像转换为具有手绘质感、明暗对比强烈、边缘清晰的单色素描图像。该过程通常依赖于深度学习模…

2026/8/5 0:00:43 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流&#xff1a;一个核心问题的诞生想象一下&#xff0c;你是一个城市供水系统的总工程师。你的城市有多个水源&#xff08;水库&#xff09;&#xff0c;需要通过一个复杂的地下管道网络&#xff0c;将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/4 13:24:41 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/4 11:41:39 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起&#xff1a;为什么我们需要互相关几年前&#xff0c;我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号&#xff0c;理论上它们接收到的声音波形应该非常相似&#xff0c;只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速&#xff1a;macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/4 13:38:24 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南&#xff1a;3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump解密工具帮你轻松解决这个困…

2026/8/4 11:09:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片&#xff1a;为英语学习 App 打造桌面级学习助手适用平台&#xff1a;HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0&#xff08;API 26 Beta&#xff09;新增了 AgentCard 智能体卡片能力&#xff0c;这是继 HMAF&#xff08;鸿蒙智能体框架&#x…

2026/8/4 13:38:40 阅读更多 →