HashSet
是 Java 中一个基于哈希表实现的集合,它继承自 AbstractSet
并实现了 Set
接口。HashSet
不保证集合中的元素有序,并且不接受重复元素。由于它是基于哈希表的,所以它支持快速的插入、删除和查找操作,具有较高的性能。HashSet
是非线程安全的,这意味着在单线程环境中它提供了较好的性能,但在多线程环境中需要额外的同步措施来保证线程安全。在现代 Java 应用中,HashSet
常用于需要快速查找和更新的场景,如去重、集合运算等。
1、 HashSet
HashSet
是 Java 集合框架中的一个基本成员,它是 java.util
包下的一个非常常用的集合类。以下是 HashSet
的设计:
设计思考:
- 需求场景:
- 在很多应用场景中,需要存储不重复的元素,并且需要快速地添加、删除和查找元素。
- 例如,在处理配置选项、用户权限、邮件地址列表等场景时,确保元素的唯一性是非常重要的。
- 现有技术局限性:
ArrayList
和LinkedList
虽然可以存储元素,但它们需要线性时间来查找元素,且不保证元素的唯一性。
- 技术融合:
HashSet
基于HashMap
实现,它结合了哈希表的快速查找特性来提供常数时间复杂度的添加、删除和查找操作,同时保证了元素的唯一性。
- 设计理念:
HashSet
提供了一个不允许重复元素的数据结构,它使用哈希表的键来存储元素,而不关心值。- 这种设计使得
HashSet
在保证元素唯一性的同时,提供了高效的操作性能。
- 实现方式:
HashSet
的每个元素都作为HashMap
的一个键存储,而对应的值是一个固定的对象(通常是一个名为PRESENT
的私有静态对象)。
2、 数据结构
图说明:
- HashSet:
- 表示
HashSet
类的实例,用于存储不重复的元素。
- 表示
- HashMap:
HashSet
的内部实现基于HashMap
。
- 数组 (Buckets) :
HashMap
使用一个数组来存储桶(Buckets),桶是用于存储Entry
对象的容器。
- 索引1, 索引2, 索引3:
- 表示数组中的具体索引位置,每个索引对应一个桶。
- Entry (链表/红黑树) :
- 每个桶可以包含多个
Entry
对象,它们通过链表或红黑树形式连接。
- 每个桶可以包含多个
- 链表 Entry:
- 在哈希冲突较少的情况下,
Entry
对象通过链表连接。
- 在哈希冲突较少的情况下,
- 红黑树 Entry:
- 当链表长度超过阈值时,链表可能会被转换成红黑树以提高搜索效率。
3、 执行流程
图说明:
- 创建 HashSet 实例:
- 初始化
HashSet
对象。
- 初始化
- 添加元素:
- 将元素添加到
HashSet
。
- 将元素添加到
- 计算元素的hashCode:
- 调用元素的
hashCode()
方法计算其哈希码。
- 调用元素的
- 确定数组索引位置:
- 根据哈希码和数组长度确定数组索引位置。
- 处理哈希冲突:
- 如果索引位置已有元素,处理哈希冲突。
- 元素添加至链表/红黑树:
- 将新元素添加至对应索引的链表或红黑树中。
- 删除元素:
- 从
HashSet
删除元素。
- 从
- 计算元素的hashCode:
- 调用元素的
hashCode()
方法计算其哈希码。
- 调用元素的
- 确定数组索引位置:
- 根据哈希码和数组长度确定数组索引位置。
- 找到对应的哈希桶:
- 定位到数组中对应的哈希桶。
- 从链表/红黑树中删除元素:
- 从对应索引的链表或红黑树中删除元素。
- 遍历 HashSet:
- 遍历
HashSet
中的所有元素。
- 遍历
- 获取数组:
- 获取
HashSet
内部的数组。
- 获取
- 遍历每个桶:
- 遍历数组的每个桶。
- 遍历链表/红黑树:
- 遍历桶内的链表或红黑树中的所有元素。
4、优点:
- 快速操作:
- 提供了常数时间复杂度的添加、删除和查找操作。
- 元素唯一性:
- 自动处理元素的唯一性,避免了重复元素的问题。
- 实现简单:
- 基于
HashMap
的实现使得HashSet
的实现非常简洁。
- 基于
5、缺点:
- 不保证顺序:
HashSet
不保证元素的任何特定顺序,特别是它不保证该顺序恒久不变。
- 内存开销:
- 相比于
ArrayList
,HashSet
可能需要更多的内存来存储哈希表结构。
- 相比于
6、使用场景:
- 需要存储不重复元素的场景,如缓存、配置选项、权限列表等。
7、类设计
8、应用案例
HashSet
通常用于去重和快速查找。这是一个用户信息管理系统,用于存储用户的唯一标识符:
import java.util.HashSet;
import java.util.Set;
// 用户类,用于表示系统中的用户
class User {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
// 省略 getter 和 setter 方法
@Override
public String toString() {
return "User{" +
"id='" + id + ''' +
", name='" + name + ''' +
'}';
}
}
// 用户信息管理系统类
class UserInfoSystem {
private Set<String> userIds;
public UserInfoSystem() {
userIds = new HashSet<>();
}
// 添加用户到系统
public void addUser(User user) {
userIds.add(user.getId());
}
// 检查用户是否已存在
public boolean isUserExist(String userId) {
return userIds.contains(userId);
}
// 获取所有用户
public Set<String> getAllUsers() {
return userIds;
}
public static void main(String[] args) {
UserInfoSystem userInfoSystem = new UserInfoSystem();
// 添加用户到系统
userInfoSystem.addUser(new User("1", "Alice"));
userInfoSystem.addUser(new User("2", "Bob"));
userInfoSystem.addUser(new User("3", "Charlie"));
// 检查用户是否存在
boolean userExists = userInfoSystem.isUserExist("2");
System.out.println("User exists: " + userExists);
// 获取并打印所有用户
Set<String> users = userInfoSystem.getAllUsers();
System.out.println("All users: " + users);
}
}