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

【某大厂一面】HashSet底层怎么实现的

HashSet 是 Java 集合框架中的一个非常常用的集合类,它实现了 Set 接口,并且底层通常是通过 哈希表HashMap)来实现的。要理解 HashSet 的底层实现,我们需要从哈希表的工作原理开始讲起。下面是对 HashSet 底层实现的详细解析。

1. HashSet 的基本特性

  • 无重复元素HashSet 不允许存储重复的元素。如果向 HashSet 中添加一个已经存在的元素,插入操作会失败。
  • 不保证元素的顺序HashSet 不保证元素的顺序,它会根据元素的哈希值决定元素存储的顺序。
  • 支持 null 元素HashSet 允许存储一个 null 元素。

2. HashSet 底层实现原理

HashSet 实际上是基于 哈希表 实现的,而哈希表的实现是通过 HashMap 类来完成的。其基本结构和工作原理如下:

  • HashSet 使用了 HashMap 作为底层存储结构。
  • 每个 HashSet 中的元素都会作为 HashMapkey 存储,而 HashMapvalue 部分则始终使用一个固定的对象(通常是 Object)作为占位符。
哈希表(HashMap)工作原理:
  1. 哈希值计算:当元素被插入到 HashSet 中时,首先会计算该元素的哈希值(使用元素的 hashCode() 方法)。哈希值决定了元素应该存放在哈希表的哪个位置。
  2. 冲突处理:如果两个不同的元素有相同的哈希值(即哈希冲突),HashMap 会通过链表(在 Java 8 之后,也可能是红黑树)来处理这些冲突。链表或树结构会存储多个哈希值相同的元素。
  3. 键值存储:在 HashMap 中,每个 key 对应着一个值(value)。在 HashSet 中,value 部分是固定的,通常不关心具体的值。
HashSet 依赖 HashMap 的特点:
  • 插入操作时,HashSet 会调用 HashMap.put(key, value) 方法来将元素作为 key 存储。
  • 查找操作时,HashSet 会调用 HashMap.containsKey(key) 方法来判断该元素是否存在。
  • 删除操作时,HashSet 会调用 HashMap.remove(key) 方法来删除元素。

3. HashSet 的常用操作分析

1. 添加元素(add()

当调用 HashSetadd() 方法时,底层实际上调用的是 HashMapput() 方法:

  • 计算元素的哈希值。
  • 判断该元素是否已经存在于哈希表中(即是否有相同的哈希值且相等的元素)。
  • 如果元素不存在,插入元素并返回 true;如果元素已经存在,返回 false
public boolean add(E e) {return map.put(e, PRESENT) == null; // map.put() 返回值为 null 表示插入成功
}

HashSet 中,PRESENT 是一个常量,通常是 new Object()

2. 查找元素(contains()

查找操作会调用 HashMapcontainsKey() 方法:

  • 计算元素的哈希值。
  • 判断该哈希值对应的桶中是否存在元素。
  • 如果存在,进一步比较元素是否相等(使用 equals() 方法),如果相等返回 true,否则返回 false
public boolean contains(Object o) {return map.containsKey(o); // 调用 HashMap 的 containsKey()
}
3. 删除元素(remove()

删除操作会调用 HashMapremove() 方法:

  • 计算元素的哈希值。
  • 查找该元素并删除。
public boolean remove(Object o) {return map.remove(o) == PRESENT; // 调用 HashMap 的 remove() 删除元素
}
4. 获取集合大小(size()

返回 HashSet 中存储的元素数量,底层是通过 HashMapsize() 方法获取的:

public int size() {return map.size(); // 调用 HashMap 的 size() 获取大小
}

4. HashSetHashMap 的关系

  • HashSet 本质上是对 HashMap 的包装,HashSet 的元素会作为 HashMapkey 存储,而 value 部分固定不变。
  • HashMapkey 使用 hashCode()equals() 方法来判断元素是否相等,所以 HashSet 中的元素也必须重写 hashCode()equals() 方法。
  • HashSet 具有与 HashMap 相同的效率特性,所有常用操作(插入、查找、删除)的时间复杂度均为 O(1),但在最坏情况下(哈希冲突严重)为 O(n)。

5. HashSet 性能特点

由于 HashSet 底层是基于哈希表的,因此它在大多数情况下提供非常高效的性能:

  • 插入操作:O(1),在没有哈希冲突的情况下,插入一个元素是常数时间。
  • 查找操作:O(1),由于哈希表是基于哈希值查找元素,查找操作通常是常数时间。
  • 删除操作:O(1),与查找操作类似,删除操作也基于哈希值进行快速定位。
  • 最坏情况下:如果所有元素都发生哈希冲突(即所有元素都被分配到同一个桶中),则所有操作的时间复杂度会退化到 O(n)。

为了减少哈希冲突,HashSetHashMap 都采用了动态扩容和哈希重哈希机制。当哈希表的负载因子(实际存储的元素数与数组容量之比)超过某个阈值时,会进行扩容(通常会将数组大小扩展为原来的 2 倍),并重新计算所有元素的哈希值并重新分配到新的数组位置。

6. HashSet 的扩容机制

HashSet 会根据负载因子和容量来动态调整内部存储数组的大小。默认情况下,HashSet 的初始容量为 16,负载因子为 0.75。

  • 容量:是哈希表的数组大小。
  • 负载因子:是哈希表的填充程度,默认值为 0.75。当哈希表中存储的元素个数超过容量的 75% 时,哈希表会进行扩容。

扩容操作会导致重新计算所有元素的哈希值,因此在性能方面可能会有一定的开销。

7. 总结

特性HashSet
底层实现基于 HashMap
是否允许重复元素不允许
是否保证顺序不保证
存储元素的方式元素作为 HashMapkey 存储,value 固定
插入操作时间复杂度O(1),最坏情况 O(n)
查找操作时间复杂度O(1),最坏情况 O(n)
删除操作时间复杂度O(1),最坏情况 O(n)

HashSet 是一个高效的集合类,适用于需要去重、无序存储的场景。它的性能与哈希表的设计紧密相关,能够提供快速的插入、查找和删除操作。

小伙伴们在开发过程中有使用心得可以再评论区一块讨论哦

http://www.lryc.cn/news/528805.html

相关文章:

  • 动手学图神经网络(3):利用图神经网络进行节点分类 从理论到实践
  • 免杀国内主流杀软的恶意样本分析
  • 第4章 基于中点电流的NPC逆变器中点电压平衡策略
  • 消息队列篇--通信协议篇--应用层协议和传输层协议理解
  • FLTK - FLTK1.4.1 - demo - animgifimage
  • 目前市场主流的AI PC对于大模型本地部署的支持情况分析-Deepseek
  • 1.2 基于深度学习的底层视觉技术
  • HTML 标题
  • SOME/IP--协议英文原文讲解3
  • Microsoft Visual Studio 2022 主题修改(补充)
  • UE(UltraEdit) 配置简易C/C++编译运行环境
  • 使用 MSYS2 qemu 尝鲜Arm64架构国产Linux系统
  • python Flask-Redis 连接远程redis
  • 在Windows系统中本地部署属于自己的大语言模型(Ollama + open-webui + deepseek-r1)
  • Haproxy入门学习二
  • Git图形化工具【lazygit】
  • node 爬虫开发内存处理 zp_stoken 作为案例分析
  • 基于Langchain-Chatchat + ChatGLM 本地部署知识库
  • 【C语言】main函数解析
  • 【QT】- QUdpSocket
  • 性能测试丨分布式性能监控系统 SkyWalking
  • SQL GROUP BY 详解
  • C语言中string.h头文件功能介绍
  • 从规则到神经网络:机器翻译技术的演进与未来展望
  • 园区管理智能化创新引领企业效能提升与风险控制新趋势
  • Python爬虫之——Cookie存储器
  • 第21节课:前端构建工具—自动化与模块化的利器
  • 企业SaaS(软件即服务)行业中AARRR
  • 为什么要学习rust
  • 观察者模式和订阅发布模式的关系