网站首页 > 技术文章 正文
1 HashSet的概念
Set是一个继承于Collection的接口,即Set也是集合中的一种。Set是没有重复元素的集合。
HashSet是Set接口典型实现,它按照Hash算法来存储集合中的元素,具有很好的存取和查找性能。底层数据结构是哈希表。
哈希表即一个元素为链表的数组,综合了数组与链表的优点。
HashSet主要具有以下特点:
- 不保证set的迭代顺序
- HashSet不是同步的,如果多个线程同时访问一个HashSet,要通过代码来保证其同步
- 集合元素值可以是null,但只能有一个null
2 HashSet的定义
2.1 构造函数
HashSet类在Java集合框架中提供了多个构造函数,用于创建不同的HashSet实例。以下是HashSet的主要构造函数及其详细讲解:
无参数构造函数:
- HashSet():此构造函数创建一个新的、空的HashSet实例。底层HashMap的初始容量为16,负载因子为0.75。随着元素的添加,HashSet会自动调整容量以容纳更多的元素。
// 创建一个 空的 HashSet 集合
HashSet<String> set = new HashSet<>();
带集合参数的构造函数:
- HashSet(Collection<? extends E> c):创建一个新的HashSet,其元素来自于指定的集合c。新创建的HashSet不会包含任何重复的元素,即便原始集合中有重复项
// 创建一个 List 集合
List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Apple");
// 创建一个 HashSet 集合,传入list集合参数
HashSet<String> set = new HashSet<>(list);
System.out.println(set); // 输出: [Apple, Cherry, Banana]
指定初始容量的构造方法:
- HashSet(int initialCapacity):创建一个新的、空的HashSet,其初始容量被设定为指定的数值。负载因子依然默认为0.75。
// 初始化容量为100的 HashSet 集合
HashSet<String> set = new HashSet<>(100);
指定初始容量和负载因子的构造函数:
- HashSet(int initialCapacity, float loadFactor):创建一个新的、空的HashSet,允许同时指定初始容量和负载因子。初始容量是你预估的最大元素数量,负载因子是用于确定何时重新调整HashMap容量的阈值,当已存储元素数量超过容量与负载因子的乘积时,HashMap会自动扩容。
// 初始容量为100,负载因子为0.8 的 HashSet 集合
HashSet<String> set = new HashSet<>(100, 0.8f);
注意:
- 初始容量(initialCapacity)必须是大于0的整数,否则会抛出IllegalArgumentException异常。
- 负载因子(loadFactor)必须是大于0且不大于1的浮点数,若传入负数或大于等于1的数也会抛出IllegalArgumentException异常。
- 设置适当的初始容量和负载因子可以帮助优化性能,减少扩容操作带来的开销。过低的初始容量会导致频繁扩容,过高则可能浪费空间。一般来说,负载因子较小意味着更早触发扩容,从而降低冲突概率,但也意味着更高的空间消耗。
3 HashSet的基本操作
3.1 添加元素
要向 HashSet 中添加元素,可以使用 add() 方法:
Set<String> colors = new HashSet<>();
colors.add("红色");
colors.add("绿色");
colors.add("蓝色");
上述代码将三种颜色添加到 HashSet 中。
3.2 删除元素
要从 HashSet 中删除元素,可以使用 remove() 方法:
Set<String> fruits = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
fruits.remove("香蕉");
上述代码删除了 HashSet 中的 “香蕉” 元素。
3.3 判断元素是否存在
可以使用 contains() 方法来检查元素是否存在于 HashSet 中:
Set<String> animals = new HashSet<>(Arrays.asList("狗", "猫", "鸟"));
boolean containsCat = animals.contains("猫");
上述代码检查了 “猫” 是否存在于 HashSet 中,并将结果存储在 containsCat 变量中。
3.4 获取集合大小
要获取 HashSet 中元素的数量,可以使用 size() 方法:
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
int size = numbers.size();
上述代码获取了 numbers 集合中元素的数量,并将结果存储在 size 变量中。
4 遍历 HashSet
遍历 HashSet 中的元素可以使用迭代器或增强型 for 循环。以下是两种遍历方式的示例:
4.1 使用迭代器遍历
Set<String> colors = new HashSet<>(Arrays.asList("红色", "绿色", "蓝色"));
Iterator<String> iterator = colors.iterator();
while (iterator.hasNext()) {
String color = iterator.next();
System.out.println(color);
}
上述代码使用迭代器遍历了 colors 集合中的元素。
4.2 使用增强型 for 循环遍历
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
for (int number : numbers) {
System.out.println(number);
}
上述代码使用增强型 for 循环遍历了 numbers 集合中的元素。
5 HashSet 的更多用法
当使用 HashSet 时,除了基本操作之外,还有一些更高级的用法和技巧可以帮助您更好地处理数据。以下是一些 HashSet 的更多用法:
5.1. 添加多个元素
您可以使用 addAll 方法一次性添加多个元素到 HashSet 中,这在需要批量插入数据时非常方便:
Set<String> fruits = new HashSet<>();
Set<String> newFruits = new HashSet<>(Arrays.asList("橙子", "草莓", "樱桃"));
fruits.addAll(newFruits);
5.2. 求交集和差集
如果您需要找出两个 HashSet 集合的交集或差集,可以使用 retainAll 和 removeAll 方法:
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
// 求交集
set1.retainAll(set2); // set1 现在包含交集 {4, 5}
// 求差集
set1.removeAll(set2); // set1 现在包含差集 {1, 2, 3}
5.3. 使用 Lambda 表达式遍历
如果您使用 Java 8 或更高版本,可以使用 Lambda 表达式来遍历 HashSet 中的元素:
Set<String> colors = new HashSet<>(Arrays.asList("红色", "绿色", "蓝色"));
colors.forEach(color -> {
System.out.println(color); // 分别输出每个颜色
});
5.4. 转换为数组
如果需要将 HashSet 中的元素转换为数组,可以使用 toArray 方法:
Set<String> colors = new HashSet<>(Arrays.asList("红色", "绿色", "蓝色"));
String[] colorArray = colors.toArray(new String[0]);
5.5. 复制 HashSet
要复制一个 HashSet,可以使用构造函数或 clone 方法:
Set<String> originalSet = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
// 使用构造函数复制
Set<String> copySet1 = new HashSet<>(originalSet);
// 使用 clone 方法复制
Set<String> copySet2 = (Set<String>) ((HashSet<String>) originalSet).clone();
5.6. 清空 HashSet
如果需要清空 HashSet 中的所有元素,可以使用 clear 方法:
Set<String> fruits = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
fruits.clear(); // 清空 fruits 集合
这些高级用法可以让您更灵活地使用 HashSet,根据具体需求选择适当的方法和技巧来处理数据。无论是处理元素的增删改查,还是进行集合操作和转换,Java 的 HashSet 集合提供了丰富的功能,以满足各种编程需求。
当使用 HashSet 时,除了基本操作之外,还有一些更多用法和技巧可以帮助您更灵活地处理数据。以下是一些 HashSet 的更多用法:
5.7. 使用迭代器删除元素
在遍历 HashSet 并删除元素时,如果直接在循环中使用 remove 方法可能会导致 ConcurrentModificationException 异常。为了避免这个问题,可以使用迭代器的 remove 方法安全地删除元素:
Set<String> fruits = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.equals("香蕉")) {
iterator.remove(); // 安全删除元素
}
}
5.8. 转换为其他集合类型
如果需要将 HashSet 转换为其他集合类型(如 ArrayList 或 LinkedList),可以使用构造函数或集合初始化的方式进行转换:
Set<String> colors = new HashSet<>(Arrays.asList("红色", "绿色", "蓝色"));
// 转换为 ArrayList
List<String> colorList = new ArrayList<>(colors);
// 转换为 LinkedList
List<String> colorLinkedList = new LinkedList<>(colors);
5.9. 比较两个 HashSet
要比较两个 HashSet 是否相等,可以使用 equals 方法。两个 HashSet 具有相同的元素,但不一定按照相同的顺序排列时,它们仍被认为是相等的。
Set<String> set1 = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
Set<String> set2 = new HashSet<>(Arrays.asList("香蕉", "苹果", "橙子"));
boolean isEqual = set1.equals(set2); // 返回 true
5.10. 创建不可变的 HashSet
如果希望创建一个不可变的 HashSet,可以使用 Collections.unmodifiableSet 方法:
Set<String> mutableSet = new HashSet<>(Arrays.asList("A", "B", "C"));
Set<String> immutableSet = Collections.unmodifiableSet(mutableSet);
通过这种方式,您可以确保其他代码无法修改 immutableSet 中的内容。
5.11. 使用 stream() 进行操作
Java 8 引入的流(Stream)可以让您更方便地对 HashSet 进行各种操作,如过滤、映射和归约等。以下是一个示例:
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
// 过滤操作,获取大于 3 的元素
Set<Integer> filteredNumbers = numbers.stream()
.filter(num -> num > 3)
.collect(Collectors.toSet());
// 映射操作,将元素加倍
Set<Integer> doubledNumbers = numbers.stream()
.map(num -> num * 2)
.collect(Collectors.toSet());
// 归约操作,计算元素的总和
int sum = numbers.stream()
.reduce(0, Integer::sum);
6 性能考虑
HashSet 的性能通常是很高的,它提供了快速的插入、删除和查询操作。但需要注意以下几点:
- 添加元素的性能:HashSet 的添加元素操作通常是很快的,但性能可能会随着负载因子的增加而下降。负载因子是一个衡量哈希表填充程度的参数,默认值是 0.75。当负载因子超过一定阈值时,HashSet 会进行扩容操作,这可能会导致性能下降。
- 查询元素的性能:HashSet 提供了快速的查询操作,因为它使用哈希表来存储元素,可以快速定位元素位置。
- 删除元素的性能:删除元素的性能通常也很高,与查询操作类似,可以快速定位要删除的元素。
7 使用注意事项
在使用 HashSet 时,需要注意以下事项:
- HashSet 不允许重复元素,如果试图添加重复的元素,重复元素将被忽略。add(Object obj):将元素添加到HashSet中,如果已存在则不添加,返回true表示添加成功,false表示元素已存在
- HashSet 不保证元素的顺序,元素在 HashSet 中是无序的。
- HashSet 是非线程安全的,如果在多线程环境下使用 HashSet,需要注意线程同步,或者考虑使用线程安全的集合类,如 ConcurrentHashSet。
- HashSet 允许存储一个 null 元素,但通常建议避免将 null 作为有效元素存储,以免混淆和错误。
- 在使用自定义对象作为 HashSet 元素时,需要正确实现 hashCode() 和 equals() 方法,以确保对象在集合中的唯一性和正确性。
- HashSet 的性能通常是很高的,但在处理大量数据时,应注意负载因子的设置,以避免频繁的扩容操作。
- 在迭代 HashSet 时,不要在迭代过程中修改集合的结构(添加或删除元素),否则可能会引发 ConcurrentModificationException 异常。
- remove(Object obj):从HashSet中移除指定的元素,返回true表示移除成功,false表示元素不存在。
- contains(Object obj):检查HashSet是否包含指定的元素,返回true表示存在,false表示不存在。
猜你喜欢
- 2024-10-16 python数据类型(python数据类型bool)
- 2024-10-16 JavaScript Set、Map、WeakSet 和 WeakMap 的区别?
- 2024-10-16 69-1-10000遗漏了哪些序号#差集#Filter...
- 2024-10-16 进入Python的世界12-常用的程序例子整理二
- 2024-10-16 Redis五种数据类型详解(redis7种数据类型)
- 2024-10-16 那些你不得不知的Redis基础类型常用操作、命令
- 2024-10-16 美团外卖iOS App冷启动治理(美团早启动)
- 2024-10-16 (Python)通过口诀记忆数组、集合、字典、元组
- 2024-10-16 【C++泛型编程】(二)标准模板库 STL
- 2024-10-16 iOS App冷启动治理:来自美团外卖的实践
- 04-27JavaScript注释:单行注释和多行注释详解
- 04-27贼好用的 Java 工具类库
- 04-27一文搞懂,WAF阻止恶意攻击的8种方法
- 04-27详细教你微信公众号正文页SVG交互开发
- 04-27Cookie 和 Session 到底有什么区别?
- 04-27教你一招,给你的店铺,网站,博客等添加“一键分享”功能
- 04-27按DeepSeek AI的规划,自学开发小程序第7天
- 04-27《JAVASCRIPT高级程序设计》第二章
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)