在 Java 的集合框架中,Set 接口用于存储无重复元素的集合。HashSet 和 TreeSet 是 Set 接口最常用的两个实现类,它们都保证了元素的唯一性,但在底层实现、元素顺序、性能表现等方面存在显著差异。
本文将围绕 HashSet 的基本用法、内部实现机制、常用方法,以及 HashSet 与 TreeSet 的核心区别与适用场景进行详细讲解,帮助开发者全面理解这两个集合类的使用方式与适用条件。
HashSet 是 Java 中 Set 接口的一个常用实现类,它基于 HashMap 实现,用于存储不重复的元素集合。
创建 HashSet
Set<String> set = new HashSet<>();
添加元素
set.add("apple");
set.add("banana");
set.add("orange");
添加元素时,HashSet 会自动去重,重复添加的元素将被忽略。
删除元素
set.remove("banana");
判断元素是否存在
if (set.contains("apple")) {
System.out.println("包含 apple");
}
遍历集合
for (String fruit : set) {
System.out.println(fruit);
}
遍历顺序是不确定的,因为 HashSet 不保证元素的顺序。
HashSet 的实现依赖于 HashMap,它将集合中的元素作为 HashMap 的键进行存储,而值则是一个固定的 Object 实例(称为 PRESENT)。
基于 HashMap 的实现
private HashMap<E, Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
由于 HashMap 的键是唯一的,因此 HashSet 中的元素也具有唯一性。
哈希算法与扩容机制
HashSet 使用对象的 hashCode() 和 equals() 方法来判断两个元素是否相等:
首先调用 hashCode() 方法获取哈希值;
哈希值决定元素的存储位置;
如果发生哈希冲突,则使用 equals() 方法判断是否为相同元素。
HashSet 会根据元素数量自动进行扩容,以保持良好的性能。
不保证元素顺序
HashSet 不维护元素的插入顺序,也不保证遍历顺序,适用于只关注元素唯一性而不关心顺序的场景。
判断集合是否为空
if (set.isEmpty()) {
System.out.println("集合为空");
}
获取集合大小
int size = set.size();
批量添加元素
set.addAll(Arrays.asList("grape", "kiwi"));
批量删除元素
set.removeAll(Arrays.asList("apple", "banana"));
清空集合
set.clear();
转换为 List
List<String> list = new ArrayList<>(set);
适用于需要将集合转换为有序结构的场景。
自定义类作为 HashSet 的元素
使用自定义类时,必须正确重写 hashCode() 和 equals() 方法,否则可能导致集合中出现重复元素。
class Person {
private String name;
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Person)) return false;
Person other = (Person) obj;
return this.name.equals(other.name);
}
}
数据去重
HashSet 最常见的用途是去除重复数据,例如:
List<String> duplicates = Arrays.asList("a", "b", "a", "c");
Set<String> unique = new HashSet<>(duplicates);
快速查找与判断
由于 HashSet 的查找时间复杂度为 O(1),适合用于快速判断某个元素是否存在于集合中。
Set<String> blackList = new HashSet<>();
blackList.add("user1");
if (blackList.contains("user1")) {
System.out.println("禁止登录");
}
集合运算(交集、并集、差集)
HashSet 支持集合运算,例如:
Set<String> set1 = new HashSet<>(Arrays.asList("a", "b", "c"));
Set<String> set2 = new HashSet<>(Arrays.asList("b", "c", "d"));
// 并集
set1.addAll(set2);
// 交集
set1.retainAll(set2);
// 差集
set1.removeAll(set2);
缓存去重
在缓存系统中,可以使用 HashSet 存储已访问过的元素,避免重复处理。
Set<String> visited = new HashSet<>();
if (!visited.contains(url)) {
// 处理 url
visited.add(url);
}
虽然 TreeSet 和 HashSet 都实现了 Set 接口,但它们在底层实现、元素顺序、性能表现、适用场景等方面存在明显差异。
底层实现不同
HashSet 基于 HashMap 实现,使用哈希算法进行存储;
TreeSet 基于 TreeMap 实现,使用红黑树结构进行排序。
元素顺序不同
HashSet 不保证元素顺序,遍历顺序是不确定的;
TreeSet 保证元素按自然顺序或自定义比较器排序。
插入、删除、查找性能不同
HashSet 的插入、删除和查找操作的时间复杂度为 O(1);
TreeSet 的插入、删除和查找操作的时间复杂度为 O(log n),但支持范围查询。
对元素的要求不同
HashSet 只依赖 hashCode() 和 equals() 方法判断是否重复;
TreeSet 要求元素实现 Comparable 接口,或在创建时传入 Comparator,否则会抛出异常。
是否支持范围查询
HashSet 不支持范围查询;
TreeSet 提供了 headSet()、tailSet()、subSet() 等方法,支持高效范围操作。
是否允许 null 元素
HashSet 允许一个 null 元素;
TreeSet 默认不允许 null 元素,否则会抛出 NullPointerException。
HashSet 是 Java 中实现快速去重、快速查找的理想集合类,它基于 HashMap 实现,具备高效的增删查性能,适合用于不需要排序、只关注元素唯一性的场景。
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
通过出发地、目的地、出发日期等信息查询航班信息。
通过站到站查询火车班次时刻表等信息,同时已集成至聚合MCP Server。火车票订票MCP不仅能赋予你的Agent火车时刻查询,还能支持在线订票能力。
通过车辆vin码查询车辆的过户次数等相关信息
验证银行卡、身份证、姓名、手机号是否一致并返回账户类型
查询个人是否存在高风险行为