实现Collection接口的Set,其中HashSet是其常用的具体类之一。
1、 Set接口继承自Collection接口,用于存储无序且不重复的元素,其特性与数学中的集合概念相似,即不允许存在重复成员。该接口有三种常见的实现类:HashSet、LinkedHashSet和TreeSet。其中,HashSet基于哈希表实现,具有较高的存取效率,是使用最为广泛的实现方式。本节将重点讲解HashSet的特点及应用方法。
2、 HashSet 是 Set 接口的一种常用实现,不保证元素的存储顺序,且不允许重复元素。若容器中已存在某个元素,则无法再加入一个完全相同的对象,确保集合中每个元素都是唯一的。
3、 先以一个实际应用为例,直观展示HashSet的使用方法。
4、 示例代码创建了一个HashSet实例,用于存储字符串对象。通过调用add方法向集合中添加多个字符串元素,随后利用Iterator迭代器对HashSet中的内容进行遍历访问,逐一处理其中的每个字符串,从而实现对集合元素的读取操作。
5、 运行程序后,控制台将显示的效果。
6、 从上图可以看出,HashSet中元素的添加顺序与迭代输出的顺序并不相同,这正体现了其存储元素具有无序性的特点。有些同学可能会疑惑:之前学习的数组以及本节提到的ArrayList,其元素排列均遵循插入顺序,为何HashSet却不再保持这种顺序?
7、 HashSet类在存储元素时,依据对象的哈希码来确定其位置。每个对象通过调用自身的hashCode方法获取哈希值,再经过固定的算法计算出在散列表中的存储索引。当系统确定了目标位置后,若该位置尚无其他元素,则直接将对象存入;若该位置已有元素存在,则会将新加入的对象与该位置上所有已存在的对象进行比较,这一过程通过调用equals方法实现。如果比较结果显示集合中已包含完全相同的对象,则不再重复添加,而是直接引用已存在的对象;若未发现相同对象,则将新对象插入到相应位置。这种机制既保证了元素的唯一性,又提高了查找和存取的效率,是HashSet实现高效操作的核心原理。
8、 HashSet的底层采用数组与链表结合的结构,其中数组的每个位置都对应一个链表,这种结构通常被称为散列表。图中左侧为存储主体的数组,每个元素存放的是链表的头节点。当添加元素时,系统会调用该元素的hashCode方法获取哈希值,并根据哈希值确定其在数组中的存储位置。若多个元素计算出的哈希值相同,就会发生哈希冲突,此时HashSet会将这些具有相同哈希码的元素链接成一个链表,并将链表的首地址存入对应数组索引位置,从而实现数据的高效存储与查找。
9、 前面的例子仅添加了字符串对象,若要加入自定义对象,应如何操作?
10、 演示如何使用HashSet存储自定义对象的实例。
11、 在Set集合中不允许存储重复元素,因此对于自定义类,必须明确定义判断重复的规则。示例代码通过重写hashCode()和equals()方法,来确定两个RemoveDuplicateObj对象是否被视为相同。这两个方法共同决定了对象在集合中的唯一性,确保集合能正确识别并去除重复实例。
12、 equals()方法用于判断两个对象是否相等。在自定义类中需根据具体属性重写该方法以实现比较逻辑。以RemoveDuplicateObj类为例,通过比较对象的name属性来判断相等性,若两个对象的name相同,则视为同一对象,从而满足业务需求中的去重条件。
13、 在散列存储结构中,hashCode()方法主要用于确定对象的存储位置。即使两个对象的hashCode值相同,也不能说明它们是同一个对象,而只能表明它们在哈希表中被映射到了相同的桶位置,可能存放在同一条链表中。对初学者而言,可以简单理解为hashCode()返回的是对象的某种地址标识,虽然实际上它并非真正的物理内存地址,而是根据对象信息计算出的一个唯一性特征值。当向集合中添加新元素时,系统会首先调用该元素的hashCode()方法,通过这个值快速定位到其应存入的索引位置,从而大大提高插入和查找效率,这种机制是哈希表高性能操作的关键基础。
14、 Set实现类通过对象的哈希值确定其存储位置,查找时只需在对应散列表位置进行少量比较即可定位。由于基于哈希机制,Set接口在添加、获取和删除元素时都具有较高的执行效率。
