Logo

郎哥编程

Set接口与HashSet类

2019-12-27 158

Set接口是Collection的子接口。Set接口存放的元素是无序的且不包含重复的对象元素,类似于数学概念的集合,集合中不允许有重复元素。Set接口有三个常用的实现类,分别是HashSet、LinkedHashSet、TreeSet。

HashSet是Set接口实现类之一,使用较为广泛。它存储的对象元素是无序的,并且不允许存储重复的对象元素。也就是当容器中已经存储一个相同的对象元素时,无法再添加一个完全相同的对象元素。

案例1:建立HashSetTest,实例化HashSet对象set,添加String对象到set集合,最后迭代输出set集合的所有String对象。

在collection包下新建HashSetTest类。代码如下:

package collection;
 
import java.util.HashSet;
import java.util.Iterator;
 
/** 
* @ClassName: HashSetTest 
* @Description: 集合框架(Set接口与HashSet类)案例1
* @author 编程训练营 
* @date 
* 
*/
 
public class HashSetTest {
 
    /**
     * @Title: main
     * @Description: Java程序入口main方法
     * @param args 参数
     *
     * @return void 返回类型 @throws
     */
 
    public static void main(String[] args) {
       // 实例化HashSet对象
       HashSet set = new HashSet();
       // 添加String对象到set集合
       set.add("a");
       set.add("b");
       set.add("c");
       set.add("d");
       set.add("e");
       // 获取set集合的迭代器
       Iterator iter = set.iterator();
       // 循环输出set集合的String对象
       while (iter.hasNext()) {
           String value = (String) iter.next();
           System.out.println(value);
       }
 
    }
 
}

案例代码实例化了一个HashSet对象,然后通过HashSet的add方法添加String对象,最后通过Iterator迭代器遍历HashSet集合。

运行上述程序,在控制台上显示效果如下图所示:

 image.png                                            

从输出结果看,HashSet添加的顺序与迭代显示的结果顺序虽然一致,这是set集合存储对象元素过少的原因。HashSet存储的对象元素是无序的,也就是对象元素的添加顺序和输出顺序并不一致。有的同学可能会问,前面学过的数组、集合类ArrayList都与加入对象元素的顺序相关,为什么HashSet与加入的对象元素顺序就无关了呢?

其实,HashSet类是根据元素的哈希码进行存储的,HashSet根据每个存储对象的哈希码值(调用hashCode方法获得),用固定的算法算出它的存储索引,把存储对象存放在一个叫做散列表的相应位置中,如果对应的位置没有其它元素,就只需要直接存入;如果该位置已经有元素了,就会将新对象跟该位置的所有对象进行比较(调用equals()方法),以查看容器中是否已经存在该对象,若不存在,就存放该对象,若已经存在,就直接使用该对象。

image.png

HashSet存储结构

HashSet的存储结构是个链表数组,每一个数组元素就是一个链表,类似这种数据结构称为散列表。上图中左边是数组,用于存储元素,该存储元素对应的数组下标是调用hashCode方法返回的存储元素的哈希码。当后加入元素的哈希码与已经加入的元素哈希码相同时,HashSet就会创建一个链表,将相同哈希码的元素存入一个链表,并将该链表的头指针存储到哈希码对应的数组元素中。

前面的示例只是添加了String对象,如果要添加一个自定义的对象,又该如何呢?

案例2:建立一个Person类,使用HashSet存储Person对象。

在PCoreUnit5项目新建set包,在set包下新建Person类。代码如下:

package set;
 
/** 
* @ClassName: Person 
* @Description: 集合框架(Set接口与HashSet类)案例2 
* @author 编程训练营 
* @date  
* 
*/
 
public class Person {
    private String name;
    private Integer age;
 
    public String getName() {
       return name;
    }
 
    public void setName(String name) {
       this.name = name;
    }
 
    public Integer getAge() {
       return age;
    }
 
    public void setAge(Integer age) {
       this.age = age;
    }
 
    // 重写父类Object的equal方法
    @Override
    public boolean equals(Object var1) {
       // 测试var1是否是Person类的实例
       if (!(var1 instanceof Person)) {
           return false;
       }
       Person person = (Person) var1;
       if (person.name == null) {
           return false;
       }
       // 判断name是否相等
       return this.name == person.getName();
    }
 
    // 重写hashCode
    @Override
    public int hashCode() {
       return this.name.hashCode();
    }
 
    // 重写toString,为打印方便
    @Override
    public String toString() {
       return "{" + this.name + "," + this.getAge() + "}";
    }
 
 
}

因为Set集合中不能加入重复的元素,所以对于自定义类,需要提供判断怎样才算重复元素的方法。在例子代码中,hashCode()和equals()方法即是用来判断Person对象是否为重复对象的标准方法。

equals()方法用来比较两个对象是否为相等的对象。在自己实现的equals()方法中,用相关的条件来进行比较。例如对于Person类,这里用name属性进行比较,name属性相同的为相等对象。

hashCode()方法是用来在散列存储结构中确定对象的存储地址的,两个对象的hashCode相同,并不一定表示两个对象就相同,只能够说明这两个对象在散列存储结构中,存储在在同一个链表中。Java初学者可以这样理解,hashCode()方法实际上返回的就是对象存储的物理地址(实际可能并不是,而是一个与对象相关的值)。 当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。

Set实现类取对象时根据对象和哈希吗值计算出该对象的存储索引,在散列表相应位置上的元素间进行少量的比较操作就可以找到。Set接口存、取、删对象都有很高的效率。

在set包下新建HashSetTest测试类。代码如下:

   package set;
 
import java.util.HashSet;
 
/** 
* @ClassName: HashSetTest 
* @Description: 集合框架(Set接口与HashSet类)案例2 
* @author 编程训练营 
* @date 
* 
*/
 
public class HashSetTest {
 
    /**
     * @Title: main
     * @Description: Java程序入口main方法
     * @param args 参数
     *
     * @return void 返回类型 @throws
     */
 
    public static void main(String[] args) {
       // 测试数据
       Person p1 = new Person();
       p1.setName("小明");
       p1.setAge(10);
       Person p2 = new Person();
       p2.setName("小红");
        p2.setAge(20);
        Person p3 = new Person();
       p3.setName("小明");
       p3.setAge(30);
 
       // 测试代码
       HashSet<Person> testSet = new HashSet<Person>();
       testSet.add(p1);
       testSet.add(p2);
       testSet.add(p3);
 
       System.out.println("testSet集合存储的Person对象个数为:" + testSet.size());
       System.out.println(testSet.toString());
 
    }
 
}

程序执行结果如下图所示:

image.png

从程序执行结果可以看出,虽然testSet集合添加了3个Person对象,但由于p1对象和p3对象的name相同,equals()方法返回的结果是true,因此p3不能加入testSet集合。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论