资讯动态

深入解析 Cangjie std.collection 之 HashSet\<T>:基于 HashMap 的高效哈希集合实战指南

发布时间:2026/10/15 18:17:09 来源:尧图企业网站定制
人工智能AI 技能文档开发工具【免费下载链接】DocAgent项目地址https://gitcode.com/Cangjie-SIG/DocAgent点击查看免费下载HashSet 是 Cangjie 标准库std.collection中基于哈希表实现的集合数据结构提供无序存储与自动去重能力其插入、删除、查找均具备较高的时间效率适合需要快速判断元素是否存在以及维护唯一性集合的场景。本文以 HashSet 类文档 为主体结合包内 API 文档、接口定义 与 可运行示例系统讲解 HashSet 的构造方式、全部成员函数、运算符重载与实战用法读完即可在 Cangjie 项目中正确、高效地使用 HashSet。一、HashSet 是什么基于 HashMap 实现的 Set 实例在 std.collection 包 中HashSet 的类声明如下见 collection_package_class.mdpublic class HashSetT : SetT where T : Hashable EquatableT { public init() public init(elements: CollectionT) public init(elements: ArrayT) public init(capacity: Int64) public init(size: Int64, initElement: (Int64) - T) }围绕该声明有几点关键语义需要理解基于 HashMap 实现HashSet 是 SetT 接口的实例其内部直接复用 HashMap 的存储机制。官方文档明确指出HashSet 的容量、内存布局、时间性能都和 HashMap 相同。也就是说理解 HashMap 的哈希表行为就等于理解了 HashSet 的底层运行原理。元素无序HashSet 中的元素没有固定顺序不可以通过索引访问也不保证元素的插入顺序。这是因为元素在哈希表中的位置由元素的哈希值决定而非插入先后。不允许重复元素向 HashSet 中添加元素时集合会根据元素的哈希值定位其在哈希表中的位置若元素已存在则添加失败从而保证唯一性。类型约束泛型参数T必须同时满足Hashable可哈希与EquatableT可判等两个约束。其中Hashable用于计算哈希值、确定存储位置EquatableT用于在哈希冲突时判断元素是否真正相等。这是使用 HashSet 前必须满足的前提条件。从接口继承关系看SetT继承自 ReadOnlySetT后者只提供contains、subsetOf等只读操作而SetT在只读基础上追加了add、remove、removeIf、clear、retain等可写操作见 collection_package_interface.md。注意与std.collection中其他集合类型一样HashSet 不支持并发安全。若需要并发场景下的集合应使用 collection.concurrent 包 提供的类型见 collection_package_overview.md。二、构造 HashSet五种构造函数详解HashSet 共提供 5 种构造函数见 HashSet 类文档覆盖空集合、指定容量、由数组/集合导入、函数批量初始化等常见场景名称原型说明init(size: Int64, initElement: (Int64) - T)通过传入的元素个数 size 和函数规则来构造 HashSet构造出的容量受 size 大小影响init()构造一个空的 HashSet初始容量为 16init(elements: ArrayT)使用传入的数组构造 HashSet容量根据数组 elements 的 size 设置init(elements: CollectionT)使用传入的集合构造 HashSet容量根据集合 elements 的 size 设置init(capacity: Int64)使用传入的容量构造一个 HashSet各构造函数的细节与约束见 collection_package_class.mdinit()构造空的 HashSet初始容量为 16。这是最常用的构造方式适合无法预估元素数量的场景之后可通过add动态插入元素。init(capacity: Int64)按指定容量预分配内部数组。若capacity小于 0会抛出 IllegalArgumentException见 init(Int64)。预先指定合理容量可减少后续扩容带来的开销。init(elements: ArrayT)/init(elements: CollectionT)将已有数组或集合中的元素一次性装入新 HashSet容量由入参的size决定过程中自动完成去重。init(size: Int64, initElement: (Int64) - T)通过一个下标到元素的映射函数批量生成元素size为元素个数initElement为初始化函数规则。若size小于 0同样抛出IllegalArgumentException。适合按规则生成一组连续数据的场景例如(i) i快速生成 0..N-1 的整数集合。容量 vs 元素个数HashSet 提供了capacity与size两个属性其中capacity返回内部数组的容量大小size返回实际元素个数。文档特别提醒容量大小不一定等于 size。哈希表为了降低冲突概率通常会让容量大于已存元素数量。三、核心成员函数完整 API 与参数说明HashSet 的全部成员函数如下原型与说明取自 HashSet 类文档异常与返回值细节见 collection_package_class.md名称原型说明prop sizepublic prop size: Int64返回此 HashSet 的元素个数add(element: T): Bool将指定元素添加到 HashSet 中若元素已存在则添加失败返回 falseadd(all!: CollectionT): Unit添加 Collection 中的所有元素至此 HashSet 中已存在的元素不添加prop capacitypublic prop capacity: Int64返回此 HashSet 的内部数组容量大小clear(): Unit从此 HashSet 中移除所有元素clone(): HashSetT克隆 HashSet返回新的独立实例contains(element: T): Bool判断 HashSet 是否包含指定元素contains(all!: CollectionT): Bool判断 HashSet 是否包含指定 Collection 中的所有元素isEmpty(): Bool判断 HashSet 是否为空iterator(): IteratorT返回此 HashSet 的迭代器remove(element: T): Bool若指定元素存在于此 HashSet 中则将其移除返回 true 表示移除成功false 表示失败remove(all!: CollectionT): Unit移除此 HashSet 中那些也包含在指定 Collection 中的所有元素removeIf(predicate: (T) - Bool): Unit传入 lambda 表达式若对元素返回 true则删除该元素reserve(additional: Int64): Unit按规则扩容详见下文retain(all!: SetT): Unit从此 HashSet 中仅保留 Set 中出现的元素集合交集语义的原地版本subsetOf(other: ReadOnlySetT): Bool检查该集合是否为其他 ReadOnlySet 的子集toArray(): ArrayT返回一个包含容器内所有元素的数组3.1 元素定位与判等contains / add / removecontains(element: T)判断集合是否包含指定元素是哈希集合最核心的查询能力——得益于哈希表结构该操作的平均时间复杂度较低无需遍历全部元素。add与remove都返回Bool表示操作是否真正生效add返回true表示新元素成功加入若元素已存在返回false且集合不变见 func add(T)。remove返回true表示移除成功元素不存在时返回false见 func remove(T)。3.2 批量操作add(Collection) / remove(Collection) / retainadd(all!: CollectionT)将集合中所有元素并入当前 HashSet重复元素自动跳过。remove(all!: CollectionT)删除当前 HashSet 中那些同样出现在给定 Collection 里的元素等价于差集的原地版本。retain(all!: SetT)保留当前 HashSet 与给定 Set 的交集部分其余元素全部删除见func retain(Set )。3.3 条件删除removeIf 与并发修改异常removeIf(predicate: (T) - Bool)接受一个 lambda 判断条件凡是满足true的元素都会被删除非常适合按规则批量清理数据例如删除所有偶数或空字符串。需要特别注意的是当predicate内部对 HashSet 本身执行了增、删、改操作时会抛出 ConcurrentModificationException见 func removeIf((T) - Bool)。也就是说谓词函数只能做判断而不能顺带修改集合否则遍历与修改并发进行会破坏迭代状态。3.4 扩容策略reserve 的完整规则reserve(additional: Int64)用于预先扩容其完整决策规则如下见 func reserve(Int64)当additional小于等于零时不发生扩容当 HashSet 的剩余容量大于等于additional时不发生扩容容量充足则无需动作当 HashSet 的剩余容量小于additional时取原始容量的 1.5 倍向下取整与additional 已使用容量中的最大值进行扩容。此外若additional 已使用容量超过Int64.Max会抛出 OverflowException。对于能够预估元素数量的场景提前调用reserve或使用指定容量的构造函数可以避免插入过程中反复触发扩容从而提升批量写入性能。3.5 其他常用能力clone / clear / toArray / isEmpty / iterator / subsetOfclone()返回一个内容相同但彼此独立的新 HashSet修改克隆体不影响原集合。clear()清空全部元素集合变为空。toArray()将集合内所有元素拷贝到ArrayT返回便于与数组 API 交互。isEmpty()判断集合是否为空等价于size 0。iterator()返回IteratorT用于遍历集合。由于集合无序遍历顺序不代表插入顺序。subsetOf(other: ReadOnlySetT)判断当前集合是否为other的子集即当前集合的每个元素都出现在other中时返回true。四、运算符重载与扩展接口集合运算一步到位HashSet 不仅提供方法形式的操作还通过运算符重载支持直观的集合代数运算见 HashSet 类文档 与 collection_package_class.md运算符原型语义(other: ReadOnlySetT): HashSetT返回包含两个集合交集元素的新集合\|(other: ReadOnlySetT): HashSetT返回包含两个集合并集元素的新集合-(other: ReadOnlySetT): HashSetT返回包含两个集合差集元素的新集合三者都接受任意ReadOnlySetT包括 HashSet、TreeSet 等并返回一个新的HashSetT不修改参与运算的原始集合。例如setA setB得到两者公共元素组成的新集合。同时HashSet 通过两个扩展接口获得额外能力extendT HashSetT : EquatableHashSetT为 HashSet 扩展 Equatable 接口提供与!运算符operator func (that: HashSetT): Bool判断两个 HashSet 是否相等。两个 HashSet 相等指的是其中包含的元素完全相等与插入顺序、内部布局无关见 operator func 。operator func !(that: HashSetT): Bool判不等逻辑上等价于!(this that)。extendT HashSetT : ToString where T : ToString当元素类型T实现 ToString 接口时HashSet 可转换为字符串提供toString(): String成员。转换结果形如[elem1, elem2, elem3]即方括号包裹、逗号分隔的格式便于调试输出与日志打印见 func toString()。五、实战add / iterator / remove 完整示例以下示例取自 sample_hashset_add_iterator_remove.md展示 HashSet 最基本的添加—遍历—删除完整流程import std.collection.* /* 测试 */ main() { var set: HashSetString HashSetString() // set: [] set.add(apple) // set: [apple] set.add(banana) // set: [apple, banana], not in order set.add(orange) // set: [apple, banana, orange], not in order set.add(peach) // set: [apple, banana, orange, peach], not in order var itset set.iterator() while(true) { var value itset.next() match(value) { case Some(v) if (!set.contains(v)) { print(Operation failed) return 1 } else { println(v) } case None break } } set.remove(apple) // set: [banana, orange, peach], not in order println(set) return 0 }该示例值得注意的细节去重验证代码在遍历时对每个元素调用contains(v)反向校验确保迭代器取出的元素确实存在于集合中这是对哈希集合增删一致性的典型自检写法。迭代器语义itset.next()返回OptionT用match匹配Some(v)/None分别处理取到元素与遍历结束None时break退出循环。这是 Cangjie 中迭代IteratorT的标准模式。无序性由于 Set 中的顺序不固定运行结果可能如下见原示例apple banana orange peach [banana, orange, peach]注意移除apple后打印集合得到的是[banana, orange, peach]而非按插入顺序排列的内容再次印证 HashSet 不保证顺序。六、关联能力与选型建议6.1 迭代器转换collectHashSetstd.collection提供了便捷的转换函数 collectHashSetT(IterableT)可将任意迭代器直接转换成 HashSetpublic func collectHashSetT(it: IterableT): HashSetT where T : Hashable EquatableT传入实现了IterableT的对象如数组、ArrayList、另一个集合的迭代器即可得到去重后的 HashSet。这在对一批数据去重并建立快速查找集合的场景中非常实用。6.2 与 TreeSet 的选型对比同为集合去重结构HashSet 与 TreeSet 的区别本质在于底层实现与排序特性见 collection_package_overview.md维度HashSetTreeSet底层实现基于 HashMap哈希表基于 TreeMap红黑树元素顺序无序不保证插入顺序自动排序有序类型约束Hashable EquatableTComparableT适用场景快速查找、去重、成员判断需要有序遍历、范围查询若只关心是否存在、是否唯一、插入删除快选 HashSet若需要按自然顺序或自定义顺序遍历元素则选 TreeSet。6.3 使用注意事项小结元素类型必须同时实现Hashable与EquatableT否则无法编译集合无序且不支持索引访问依赖顺序的算法请先toArray()后自行排序或改用 TreeSet非并发安全多线程共享同一 HashSet 时需要外部同步或改用collection.concurrent包removeIf的谓词内禁止修改集合本身否则抛出ConcurrentModificationException预估到元素规模较大时优先使用指定容量构造或reserve预扩容减少哈希表重建开销。结语HashSet 是 Cangjiestd.collection中兼顾去重与查询效率的核心容器它复用了 HashMap 的哈希表机制以无序 唯一的语义覆盖了成员判断、批量去重、集合代数运算等高频需求并通过运算符重载、|、-与扩展接口、!、toString提供了接近原生语法的体验。掌握本文所述的构造方式、容量与扩容规则、迭代与条件删除的边界约束即可在实战中正确驾驭这一数据结构。赞分享人工智能AI 技能文档开发工具【免费下载链接】DocAgent项目地址https://gitcode.com/Cangjie-SIG/DocAgent点击查看免费下载相关推荐仓颉 std.collection HashMap 类详解哈希表键值映射的完整实战指南仓颉 std.collection HashMap 类详解哈希表键值映射的完整实战指南 HashMap 是仓颉标准库 std.collection 包中 Ma人工智能AI 技能文档开发工具Swift 哈希集合HashSet实现指南基于字典构建高性能 Set 与并集、交集、差集运算Swift 哈希集合HashSet实现指南基于字典构建高性能 Set 与并集、交集、差集运算 导读 集合Set是一类元素唯一、顺序无关的容器是比示例工程教程深入 Rust 常用集合Vec 向量、String 字符串与 HashMap 哈希映射实战指南深入 Rust 常用集合Vec 向量、String 字符串与 HashMap 哈希映射实战指南 《The Rust Programming Language》教程文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑