深入解析HashMap:从哈希函数到红黑树,揭秘Java核心数据结构
1. 从“键值对”到“数组链表/红黑树”HashMap的设计哲学如果你写过Java或者用过任何现代编程语言那么HashMap对你来说一定不陌生。它就像一个超级高效的“字典”或“电话本”给你一个名字Key你就能瞬间找到对应的电话号码Value。这种“瞬间”查找的能力是HashMap最迷人的地方。但你是否想过这个看似简单的“键值对”容器底层是如何做到如此高效的为什么我们常说HashMap的查询时间复杂度是O(1)当数据量变大时它又是如何保持性能的今天我们就抛开那些枯燥的API文档从一个Java老兵的视角深入HashMap的“五脏六腑”看看它到底是怎么工作的。HashMap的核心目标只有一个用最快的速度通过一个不重复的键Key找到对应的值Value。为了实现这个目标它巧妙地结合了数组、链表和红黑树这三种数据结构。数组提供了O(1)的随机访问能力这是高速查询的基石链表解决了数组下标冲突的问题而红黑树则是在链表过长时用来挽救性能的“终极武器”。理解这三者的协同工作是理解HashMap底层原理的关键。接下来我们将从最基础的哈希函数开始一步步拆解这个经典容器的实现细节。2. 哈希函数与数组索引定位高速访问的起点当我们调用map.put(“张三”, “13800138000”)时HashMap要做的第一件事就是决定把这个键值对放在内部数组的哪个位置上。这个过程就是“哈希”。2.1 哈希码的计算与扰动在Java中每个对象都有一个hashCode()方法。对于字符串“张三”它会计算出一个int类型的哈希码。但是直接使用这个哈希码是不行的。首先hashCode()方法返回的是一个32位的整数范围从-2^31到2^31-1而我们内部的数组在HashMap中称为table长度通常远小于这个范围比如默认长度16。其次如果直接使用一些实现不佳的hashCode()方法可能导致高位的变化无法影响到最终的索引值从而增加哈希冲突的概率。因此HashMap引入了“扰动函数”。在JDK 8的实现中这个函数非常简洁而精妙static final int hash(Object key) { int h; return (key null) ? 0 : (h key.hashCode()) ^ (h 16); }这段代码做了什么呢它获取了key的原始哈希码h然后让h与h无符号右移16位后的结果进行异或^操作。右移16位相当于把高16位移动到了低16位。异或操作能保证高16位和低16位的特征都被混合起来。这样做的核心目的是让哈希码的高位特征也能参与到后续的索引计算中从而让哈希值分布得更均匀减少后续的哈希冲突。你可以把它理解为一种“搅拌”操作让原材料混合得更充分。注意这里有一个关键点HashMap允许键Key为null并且规定null的哈希值为0它会被放置在数组的第一个位置如果该位置没有冲突的话。2.2 索引的最终计算得到扰动后的哈希值hash后如何映射到具体的数组下标呢HashMap的数组长度n总是2的幂如16, 32, 64…。它使用了一个非常高效的操作index (n - 1) hash这里是按位与操作。因为n是2的幂所以n-1的二进制形式就是一串连续的1例如16-115二进制是1111。(n-1) hash这个操作本质上就是取哈希值hash的低几位。这相当于一个高效的取模运算hash % n但位运算的速度远快于取模运算。为什么数组长度必须是2的幂计算高效如上所述用(n-1) hash代替hash % n位运算效率极高。分布均匀当n是2的幂时n-1的二进制低位全是1这使得操作的结果能均匀地利用哈希值的每一位减少冲突。如果n不是2的幂比如是10那么n-1的二进制是1001中间有0会导致某些数组位置永远无法被映射到例如哈希值第二位是1的位置永远无法映射到造成数组空间浪费和冲突加剧。一个简单的例子假设hash(“张三”)扰动后是1010 1101 0011 0101仅为示意数组长度n16n-115二进制1111。 计算索引(1111) (1010 1101 0011 0101) 0101二进制即十进制5。所以键值对“张三”, “138…”就会被尝试放在数组下标为5的位置。3. 哈希冲突的解决链表与红黑树的演进理想情况下每个不同的Key都计算出唯一的索引直接放入数组对应位置。但现实是骨感的不同的Key完全可能计算出相同的索引这就是哈希冲突。比如“张三”和“李四”经过哈希和取模后都指向了数组下标5。HashMap解决冲突的方法经历了从单纯链表到“链表红黑树”的演进。3.1 链表拉链法最直接的解决方案在JDK 8之前HashMap处理冲突只有一种方式链表拉链法。数组的每个位置不再只存储一个元素而是存储一个链表的头节点。当发生冲突时新的键值对会以链表节点的形式插入到对应位置链表的头部头插法。这种方式实现简单在数据量不大、冲突不严重时效率尚可。但是链表拉链法有一个致命的弱点如果某个数组下标位置发生的冲突非常严重链表变得非常长例如在极端情况下所有数据都哈希到同一个位置那么查询时就需要遍历整个链表时间复杂度退化为O(n)HashMap的高性能优势将荡然无存。在JDK 7及以前这甚至是导致拒绝服务攻击HashDoS的一个潜在漏洞攻击者可以精心构造大量哈希冲突的Key使HashMap的性能急剧下降。3.2 红黑树的引入性能的守护者为了解决长链表导致的性能退化问题JDK 8对HashMap的实现做了重大优化引入了红黑树。红黑树是一种自平衡的二叉查找树它能保证在最坏情况下基本的动态集合操作查找、插入、删除的时间复杂度为O(log n)这远比O(n)的链表要好。链表何时会转为红黑树HashMap设定了两个关键的阈值TREEIFY_THRESHOLD值为8。当某个数组位置桶中的链表长度超过8时HashMap会判断是否要将这个链表转换为红黑树。MIN_TREEIFY_CAPACITY值为64。链表转红黑树还有一个前提条件当前HashMap的数组总长度容量必须达到64。如果容量小于64即使链表长度超过8HashMap也会选择先进行数组扩容resize试图通过扩大数组分散元素来缩短链表长度而不是立即树化。因为在小容量下扩容的收益可能比树化更高。红黑树何时会退化为链表同样为了节省空间当红黑树中的节点数由于删除操作而减少到小于等于6UNTREEIFY_THRESHOLD时红黑树会退化为链表。为什么阈值是8和6这是一个基于统计学概率的设计。在理想的随机哈希情况下一个桶中链表长度达到8的概率已经微乎其微泊松分布下小于千万分之一。因此在绝大多数正常使用的场景中我们根本不会遇到红黑树。这个设计是在极端情况哈希碰撞攻击或糟糕的哈希函数下的性能保障和正常情况下的空间开销之间取得的一个完美平衡。而退化的阈值设为6小于8是为了避免在节点数在8附近频繁地树化和退化造成不必要的性能抖动。3.3 插入流程详解putVal()的核心逻辑理解了数据结构我们再来看最核心的put方法内部是putVal的详细步骤这能帮你把前面所有知识点串联起来判断表是否为空如果内部数组table为空或者长度为0则先调用resize()方法进行初始化扩容。计算索引通过(n-1) hash计算键值对应放入的数组索引i。检查首节点如果table[i]位置为空直接新建一个普通链表节点Node放进去插入完成。如果table[i]位置不为空则说明发生了哈希冲突进入下一步。处理冲突情况A首节点Key匹配。检查table[i]处节点的Key是否与待插入Key相同通过equals方法判断。如果相同则找到了旧节点准备覆盖其Value。情况B首节点是树节点。如果table[i]处的节点是TreeNode红黑树节点则调用红黑树的插入方法putTreeVal。情况C遍历链表。否则开始遍历该位置的链表。在遍历过程中如果找到Key相同的节点则标记为旧节点。如果遍历到链表尾部仍未找到则在尾部插入新节点。插入后立即检查链表长度是否达到TREEIFY_THRESHOLD8。如果达到且数组总容量 MIN_TREEIFY_CAPACITY64则调用treeifyBin方法将整个链表转换为红黑树。处理覆盖如果步骤3或4中找到了已存在的Key旧节点则用新Value替换旧Value并返回旧Value。结构修改与扩容检查这是一个新插入的节点因此增加修改次数modCount和元素总数size。然后判断size是否超过了容量 * 负载因子默认容量*0.75。如果超过则调用resize()方法进行扩容。这个流程清晰地展示了HashMap如何根据不同的情况在数组、链表、红黑树三者之间进行选择和转换。4. 动态扩容机制resize()的智慧负载因子Load Factor和扩容Resize是HashMap调节性能和空间利用率的核心杠杆。默认负载因子是0.75这是一个经验值。为什么是0.75这是一个在时间和空间成本上的折衷。负载因子越高比如0.9数组的利用率就越高空间开销越小但哈希冲突的概率会显著增加导致链表变长查询性能下降。负载因子越低比如0.5冲突减少查询性能好但会有大量的数组空间被浪费空间利用率低。0.75这个值在大量实验统计下被认为是能在时间和空间上取得较好平衡的点。扩容触发条件当HashMap中存储的键值对数量size超过容量capacity * 负载因子loadFactor时就会触发扩容。例如默认初始容量16负载因子0.75那么当放入第13个元素16*0.7512时就会触发扩容。扩容做了什么resize()方法主要做两件大事创建新数组将数组容量扩大为原来的2倍因为容量必须是2的幂所以是2倍扩容比如16-32。数据迁移重哈希遍历旧数组中的每一个桶可能是空、单个节点、链表或红黑树将里面的所有节点重新计算索引并放置到新数组的对应位置。JDK 8的优化高效的数据迁移在JDK 8的扩容中有一个非常巧妙的优化。由于扩容是2倍新数组的容量newCap是旧数组容量oldCap的2倍。那么新索引的计算公式是(newCap-1) hash。观察二进制可以发现newCap-1相比oldCap-1只是在高位多了一个1。关键结论来了一个节点在新数组中的位置要么和原位置相同要么是原位置加上旧容量oldCap。具体是哪种情况取决于该节点哈希值在oldCap对应二进制位上的值是0还是1。示例 旧容量 oldCap 16 (二进制 10000) 旧索引计算hash (16-1) hash 1111 新容量 newCap 32 (二进制 100000) 新索引计算hash (32-1) hash 11111 对比新旧索引多出来的一位就是 hash 10000即 oldCap。 如果 (hash oldCap) 0 则该节点新索引 原索引。 如果 (hash oldCap) ! 0 则该节点新索引 原索引 oldCap。基于这个原理JDK 8在迁移链表时不需要像JDK 7那样对每个节点重新计算哈希而是可以直接将原链表拆分成两个子链表一个保持原索引低位链表另一个索引为原索引oldCap高位链表。然后将这两个链表直接挂到新数组的对应位置。这个过程只需要遍历一次链表效率非常高。对于红黑树也有类似的优化拆分逻辑。5. 线程安全问题与ConcurrentHashMap的启示HashMap是一个非线程安全的容器。这在它的源码注释里写得清清楚楚。在多线程环境下使用HashMap最常见的问题就是数据不一致和死循环在JDK 7的头插法扩容时尤其突出。为什么线程不安全我们回顾一下putVal和resize的流程其中包含了多个“检查-然后-操作”的步骤例如检查桶是否为空、检查链表长度、修改size等。这些操作在多线程环境下都不是原子的。两个线程可能同时检查到同一个桶为空然后都认为自己可以插入导致其中一个线程的写入被覆盖。更严重的是在JDK 7的扩容过程中由于采用头插法反转链表在多线程同时扩容时可能产生循环链表导致后续的get操作陷入死循环。JDK 8虽然通过尾插法解决了死循环问题但数据覆盖等不一致性问题依然存在。解决方案ConcurrentHashMap这也就是为什么在并发编程中我们几乎总是使用ConcurrentHashMap来代替HashMap。ConcurrentHashMap在JDK 7中采用分段锁Segment机制而在JDK 8中则做了更彻底的优化使用了**synchronized CASCompare-And-Swap** 的方式。在JDK 8的ConcurrentHashMap中对于数组元素的插入它使用synchronized锁住链表的头节点或红黑树的根节点而不是锁住整个Map。这大大提高了并发粒度。对于size等变量的更新广泛使用了CAS操作这是一种无锁的乐观并发控制性能更高。其get操作通常完全不需要加锁因为Node的val和next都被volatile修饰保证了可见性。理解HashMap的线程不安全性和ConcurrentHashMap的解决方案能让你在设计和面试中清晰地知道何时该用谁。简单来说单线程用HashMap追求极致性能多线程并发场景无脑选择ConcurrentHashMap。6. 关键参数与性能调优实践在实际使用中我们虽然不常去修改HashMap的内部参数但理解它们对于写出高性能的代码和进行问题排查至关重要。1. 初始容量initialCapacity这是创建HashMap时内部数组的初始大小。默认是16。如果你能提前预估要存储的键值对数量最好通过构造函数new HashMap(expectedSize)来指定一个初始容量。这可以避免或减少后续扩容的次数。一个简单的估算公式是initialCapacity (expectedSize / loadFactor) 1。例如预计要存100个元素负载因子0.75那么100/0.75≈133取下一个2的幂就是256。指定new HashMap(256)会比使用默认16然后多次扩容高效得多。2. 负载因子loadFactor除非有非常特殊的场景否则不建议修改默认的0.75。降低负载因子如0.5会提升查询速度但浪费内存提高负载因子如0.9会节约内存但增加查询时间。通常默认值是最佳选择。3. 键Key对象的设计这是影响HashMap性能最容易被忽视也最关键的一点。HashMap的性能严重依赖于键的hashCode()和equals()方法。hashCode()必须保证对同一个对象返回相同的值同时尽可能让不同的对象返回不同的值分布均匀。一个好的哈希函数能极大减少冲突。对于自定义类推荐使用Objects.hash(field1, field2, ...)来计算。equals()必须与hashCode()保持一致即如果两个对象equals()返回true那么它们的hashCode()必须相等。反之hashCode()相等的两个对象equals()不一定为true这就是哈希冲突。一个常见的坑使用可变对象作为Key。例如用一个ArrayList作为Key放入HashMap后又修改了这个ArrayList的内容。这会导致其hashCode()改变后续再也无法通过get()方法正确找到它因为计算出的索引变了但它又确实存在于Map的某个错误位置造成内存泄漏和逻辑错误。因此最佳实践是使用不可变对象如String、Integer或确保作为Key的对象在其作为Key的生命周期内不会改变其用于计算hashCode和equals的字段。7. 源码层面的精妙设计赏析最后我们跳出使用层面看看HashMap源码中一些体现设计智慧的地方。1. 容量总是2的幂我们前面已经分析了其好处高效取模、分布均匀。构造函数中如果你传入一个不是2的幂的初始容量比如new HashMap(10)HashMap会通过tableSizeFor方法将其转换为大于等于该值的最小的2的幂16。这个方法同样使用了精妙的位操作static final int tableSizeFor(int cap) { int n cap - 1; n | n 1; n | n 2; n | n 4; n | n 8; n | n 16; return (n 0) ? 1 : (n MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n 1; }这个方法的作用是将cap最高位1之后的所有位都置为1然后加1从而得到2的幂。例如cap10n9(1001)经过一系列右移和或操作后最终得到15(1111)再加1得到16。2. 树化与退化的阈值 hysteresis设置树化阈值8和退化阈值6中间有个差值2这是一种“迟滞”hysteresis设计。目的是防止在节点数在阈值边界频繁增删时发生链表和红黑树之间频繁且昂贵的转换。比如一个桶的节点数在7、8、9之间波动如果没有迟滞可能会频繁触发树化和退化得不偿失。3. 红黑树节点的特殊处理当链表转为红黑树时TreeNode节点不仅维护了红黑树结构父、左、右指针还通过prev和next指针维护了原链表的顺序。这样在退化回链表时或者进行遍历时可以保持与链表一致的顺序插入顺序或访问顺序取决于Map的类型。这种设计体现了在复杂性和功能性之间的权衡。HashMap的源码是Java集合框架中最值得品读的经典之一。它用相对简洁的代码实现了高效、健壮的数据结构并且在JDK的迭代中不断优化。理解它的原理不仅能让你在面试中游刃有余更能让你在编写高性能、高可靠性的代码时做出最合适的选择。下次当你再写下MapString, Object map new HashMap()时希望你的脑海里能清晰地浮现出它背后那个精妙而高效的世界。

相关新闻

Python逻辑运算符and的短路特性与实用技巧

Python逻辑运算符and的短路特性与实用技巧

1. Python逻辑运算符and的核心特性解析 在Python编程中,逻辑运算符and是构建条件判断的基础工具之一。与or和not不同,and运算符有着独特的"短路求值"特性:当左操作数为假时,解释器会直接返回左操作数而不再计算右操作数…

2026/8/1 14:28:07 阅读更多 →
高温压力传感器与变换器系统的电气接口设计方法和工程集成实践指南

高温压力传感器与变换器系统的电气接口设计方法和工程集成实践指南

压力传感器从产品到工程应用,需要经过电气接口设计和系统集成这一关键环节。在高温环境下,接口的可靠性、信号的完整性和系统的可维护性都面临更大的挑战。本文以200℃高温压力传感器变换器系统为对象,从工程实践角度阐述系统集成设计中的关键…

2026/8/1 14:28:07 阅读更多 →
滑动窗口算法详解:从原理到实战,攻克LeetCode高频面试题

滑动窗口算法详解:从原理到实战,攻克LeetCode高频面试题

1. 从一道高频面试题说起:为什么滑动窗口是解题利器如果你刷过LeetCode或者准备过技术面试,大概率遇到过这样一类问题:给你一个字符串或者数组,让你找出其中“不重复字符的最长子串”、“和大于等于目标值的最短子数组”或者“包含…

2026/8/1 14:28:07 阅读更多 →

最新新闻

DP83848以太网PHY芯片电路设计与嵌入式网络接口开发实战

DP83848以太网PHY芯片电路设计与嵌入式网络接口开发实战

1. 项目概述:从一颗PHY芯片到一块完整的以太网板卡如果你玩过ESP32、树莓派Pico这类没有原生以太网接口的微控制器,或者想给一些老旧的工业设备增加网络功能,那你大概率听说过或者搜索过“DP83848”这个名字。它不是什么新潮的玩意儿&#xf…

2026/8/1 15:15:29 阅读更多 →
工业级RS485转以太网网关设计:从硬件选型到软件架构全解析

工业级RS485转以太网网关设计:从硬件选型到软件架构全解析

1. 项目概述:从串行总线到以太网的桥梁如果你在工业自动化、智能楼宇或者物联网设备集成领域摸爬滚打过,一定对RS485这根“老将”不陌生。它稳定、抗干扰、能一根线上挂几十上百个设备,是现场设备层通信的绝对主力。但它的“慢”和“距离限制…

2026/8/1 15:15:29 阅读更多 →
Spring事务回滚机制深度解析:从原理到实战避坑指南

Spring事务回滚机制深度解析:从原理到实战避坑指南

1. 项目概述:从一次线上事故说起 去年我们团队遇到一个典型的线上问题:一个用户下单后,积分扣减成功了,但订单记录却没生成。这直接导致了用户积分被扣,却查不到任何订单,客服电话被打爆。事后排查&#xf…

2026/8/1 15:15:29 阅读更多 →
从100%到21%:洛雪音乐音源成功率背后的技术真相

从100%到21%:洛雪音乐音源成功率背后的技术真相

从100%到21%:洛雪音乐音源成功率背后的技术真相 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 当你在洛雪音乐中搜索周杰伦的最新单曲,为什么有些音源能瞬间返回FLAC无损音…

2026/8/1 15:15:29 阅读更多 →
如何快速下载Zenodo数据:3步掌握科研数据获取终极技巧

如何快速下载Zenodo数据:3步掌握科研数据获取终极技巧

如何快速下载Zenodo数据:3步掌握科研数据获取终极技巧 【免费下载链接】zenodo_get Zenodo_get - a downloader for Zenodo records 项目地址: https://gitcode.com/gh_mirrors/ze/zenodo_get zenodo_get是一个专为Zenodo平台设计的Python下载工具&#xff0…

2026/8/1 15:15:29 阅读更多 →
【电脑科普】内存条的简单科普!

【电脑科普】内存条的简单科普!

内存条是连接CPU 和其他设备的通道,起到缓冲和数据交换作用。 当CPU在工作时,需要从硬盘等外部存储器上读取数据,但由于硬盘这个“仓库”太大,加上离CPU也很“远”,运输“原料”数据的速度就比较慢,导致CPU…

2026/8/1 15:14:29 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/8/1 0:00:48 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/8/1 13:02:46 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/8/1 5:19:34 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/8/1 10:33:33 阅读更多 →

月新闻

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

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

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

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

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

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

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

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

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

2026/8/1 0:00:48 阅读更多 →