欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java集合HashSet基本使用详解

程序员文章站 2022-05-20 10:34:26
...

Java集合HashSet基本使用详解

HashSet特点

因为 HashSet是实现了Set接口的,所以特点和Set差不多,都是无序、无下标、元素不重复

存储结构

HashSet的存储结构在jdk1.7之前是基于数组+链表实现的,在jdk1.8之后是基于数组+链表+红黑树来实现的

存储过程

1、根据hashcode计算保存的位置,如果此位置为空,则直接进行保存,如果不为空则进行第二步
2、执行equals方法,如果equals方法为true,则认为是重复,不继续添加,否则进行添加而形成链表

这里对上面的话简单的进行翻译一下,大家可以设想一下在火车站买门票的情形,多个窗口下排着不同的人群。窗口可以理解为一个连续的数组,分别有自己的下标,而人群则可以理解为挂在下面的元素,存储的结构为链表。所以在整个存储的过程中,根据hashcode值来判断保存在数组的哪一个位置,每个数据元素都有属于自己的hashcode值,切不同元素之间的值可能相同也可能不同。如果hashcode值相同的数则挂在同一个数组下标下。

测试案例

这里我自己新建了一个Person类来进行测试:

package com.HashSet;

public class Person {
    private String name;//姓名属性
    private int age;//年龄属性

    @Override
    public String toString() { //重写toString方法,没啥好说
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person(String name, int age) { //重写构造方法,没啥好说
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

添加和删除数据:

public class Main {
    public static void main(String[] args) {
        HashSet<Person> hashSet = new HashSet<Person>();
        Person p1 = new Person("张三",18);
        Person p2 = new Person("李四",20);
        Person p3 = new Person("王五",22);
        Person p4 = new Person("赵六",21);

        //添加元素
        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);
        hashSet.add(p4);
//        hashSet.add(p4); //重复添加元素,size不会进行改变

        System.out.println("size = "+hashSet.size());//4
        System.out.println(hashSet.toString());
        
        //删除
        hashSet.remove(p1);
        System.out.println("size = "+hashSet.size());//3
        
    }
}

在这里的打印结果如下,没啥好说的:

size = 4
[Person{name='李四', age=20}, Person{name='赵六', age=21}, Person{name='张三', age=18}, Person{name='王五', age=22}]
size = 3

注意事项

下面,可以将问题进行引申,如果我添加写的是 add(new Person(“王五”,22)),会不会重复进行添加?size大小会不会不变?如果删除的是remove(new Person(“王五”,22))那么size会不会减小?

答案是:add会添加成功,size会变大,remove之后的第一个王五没有 被 移除,size大小没有发生改变(不信的同学可以自己跑一遍代码进行演示)。

在这里我们得明白我们想要的功能是什么,按照Set元素的不可重复性,我们应该认为在添加了相同的元素以后size的大小是不能发生改变的,而再删除了王五以后size的大小应该是会变小,在这里却没有得到实现,所以我们应该好好思索一下这个问题。

产生问题的原因就是HashSet的数据存储过程,前面也说了,数据是根据hashcode和equals方法来进行存储的,而我们想要在逻辑上实现我们想要的结果,就必须得对hashcode方法和equals方法按照我们的方式进行重写。

package com.HashSet;

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    //重写equals方法
    @Override
    public boolean equals(Object o) {
        if(o==null) //如果对象为空
            return false;
        if(o==this) //如果对象是本身
            return true;
        if(o instanceof Person) //如果对象属于Student类并且属性相等
        {
            Person p = (Person) o;
            if(p.getName().equals(this.name)&&p.getAge()==this.age)
                return true;
        }
        return false;
    }
    //重写hanshCode方法
    @Override
    public int hashCode() {
        int n1 = this.name.hashCode(); //得到name的hashcode值
        int n2 = this.age;
        return n1+n2;
    }

    @Override
    public String toString() { //重写toString方法,没啥好说
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person(String name, int age) { //重写构造方法,没啥好说
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

如此一来,问题就可以得到解决了。当然这是我自己重写的hashcode方法和equals方法,如果大家根据自己的需求嫌麻烦的话可以IDE会有自动帮你写的。就想insert构造方法和get、set方法一样。不知道的同学可以到网上去搜索,这里用的是InterJ IDEA,快捷键导入的话是 ALT+INSERT.

后面的元素遍历和判空等基本操作这里就不再进行演示了,因为和之间我写的ArrayList文章差不多,大家如果有可以去参考一下,值得注意的是和ArrayList比起来HashSet进行遍历的话只能用增强for循环和迭代器,不支持for循环,因为没有下标嘛~