Set集合

Set

Set集合分类

根据不同集合的底层数据结构以及其特点,Set集合分为三类

  1. HashSet
  • 底层数据结构为哈希表,存取无序且元素唯一
  1. LinkedHashSet
  • 底层数据结构为链表和哈希表,存取有序且元素唯一
  1. TreeSet
  • 元素唯一 且可以通过自然排序或比较器排序,对元素进行排序。

常用方法

Set属于Collection的子接口,所以Set集合可以直接调用Collection的方法,现在介绍Set子接口特有的方法:

Set常用方法功能
boolean add(E e)如果 set 中尚未存在指定的元素,则添加此元素
void clear()移除此 set 中的所有元素
boolean contains( )如果 set 包含指定的元素,则返回 true
boolean isEmpty( )判断Set集合是否为空集合
Iterator<E> irerator( )返回一个在此Set集合元素上进行迭代的迭代器
boolean remove(Object o)如果Set集合中存在指定元素,则将其移除
int size( )返回Set集合中的元素数
Object[] toArray( )返回一个包含Set集合中所有元素的数组

HashSet

特点

  • HashSet集合底层数据结构为哈希表,存取无序且元素唯一
    当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。

原理

  • HashSet 集合判断两个元素相等的标准:
    两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。

当我们定义一个HashSet集合,向集合中添加集合Integer类对象时,当其中有重复元素,之后添加的会将原有的覆盖:

public class Test {
    public static void main(String[] args) {
        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(new Integer(20));
        hashSet.add(new Integer(20));
        hashSet.add(new Integer(10));
        hashSet.add(new Integer(10));
         System.out.println(new Integer(20).equals(new Integer(20)));
        System.out.println(new Integer(10).equals(new Integer(10)));
        System.out.println(hashSet);
    }
}

在这里插入图片描述
因为两个Integer对象通过equal方法返回是true,所以HashSet集合只会存储一个对象。
现在我们用HashSet集合存储几个学生类的对象:

  • 学生类:
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Studeng{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
        }
  }
  • Set集合:
public class Test {
    public static void main(String[] args) {
        HashSet<Student> hashSet = new HashSet<>();
        Student stu1 = new Student("小明",20);
        Student stu2 = new Student("小红",18);
        Student stu3 = new Student("小明",20);
        Student stu4 = new Student("小红",18);
        hashSet.add(stu1);
        hashSet.add(stu2);
        hashSet.add(stu3);
        hashSet.add(stu4);
        System.out.println(stu1.equals(stu3));
        System.out.println(stu2.equals(stu4));
        System.out.println(hashSet);
    }
}

在这里插入图片描述
会发现两个成员变量完全相同的学生类对象存储到HashSet集合中,并没有元素唯一,这是因为equal()方法默认比较的是两个对象的地址值,并没有比较对象的成员变量,所以当我们想达在HashSet集合中通过比较成员变量实现元素唯一时,我们需要重写hashCode()和equals()方法,重写后HashSet就能根据成员变量值存储唯一元素。

  • 重写equal()和hashCode()方法 , 可通过快捷键Alt+Insert
 @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

这时HashSet集合就会根据成员变量值存储唯一元素,运行结果:
在这里插入图片描述

LinkedHashSet

特点

  • 底层数据结构有两个 链表和哈希表
    ​ 链表保证有序 、哈希表保证元素唯一

LinkedHashSet集合和HashSet集合同样可以保证元素唯一性,LinkedHashSet集合还能通过链表保证元素的输入输出有序。现在用HashSet集合和LinkHashSet集合按照相同顺序存储相同的字符元素:

  • HashSet集合
public class Test {
    public static void main(String[] args) {
        HashSet<Character> hashSet = new HashSet<>();
        hashSet.add('e');
        hashSet.add('d');
        hashSet.add('a');
        hashSet.add('c');
        hashSet.add('b');
        System.out.println(hashSet);
    }
}

在这里插入图片描述
会发现数组中存储的顺序和我们添加元素的顺序并不相同,是因为HashSet集合是根据哈希表对元素进行排序。

  • LinkedHashSet集合
public class Test {
    public static void main(String[] args) {
        LinkedHashSet<Character> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add('e');
        linkedHashSet.add('d');
        linkedHashSet.add('a');
        linkedHashSet.add('c');
        linkedHashSet.add('b');
        System.out.println(linkedHashSet);
    }
}

在这里插入图片描述
LinkedHashSet集合对元素的存储会通过链表的形式,在元素唯一的基础上,使元素在集合中存储的顺序和添加顺序一致。

TreeSet

特点

  • 元素唯一,并且可以对元素进行排序

原理

TreeSet中存储元素,不仅可以达到元素的唯一性,还能对元素进行排序。
这是因为TreeSet集合的底层数据结构为二叉树(红黑树)。
在这里插入图片描述
当存储一个新的元素时,会和根节点的元素值进行比较。如果大于,则会存储到根节点的右边,小于则会存到坐标左边,值相等则不储存。这样就达到了元素唯一且排序的目的。

自然排序

现在在TreeSet中存储一些Integer类型的数据:

ublic class Test {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(20);
        treeSet.add(10);
        treeSet.add(50);
        treeSet.add(20);
        treeSet.add(10);
        treeSet.add(30);
        System.out.println(treeSet);
    }
}

在这里插入图片描述
这时TreeSet集合自动覆盖掉了重复元素并且将元素排序。
那我们现在存储一些学生类的对象:

public class Test {
    public static void main(String[] args) {
        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(new Student("小明",20));
        treeSet.add(new Student("小红",18));
        treeSet.add(new Student("小明",23));
        treeSet.add(new Student("小红",18));
        treeSet.add(new Student("李雷",25));
        System.out.println(treeSet);
    }
}

这时我们运行会发现系统报错:ClassCastException(Java强制类型转换异常)
原因是当我们在TreeSet存储数据时,TreeSet想要进行自然排序,前提是我们存储的元素必须实现Comparable接口,否则无法进行自然排序。换句话说我们存储Integer类型,Comparable接口可以判断出元素值的大小,但是存储Student对象,接口无法判断。
如果我们想要可以将对象按照某个标准进行排序,那就需要让学生类来实现Comparable接口,要实现接口,需要重写接口的compareTo方法。

  • 例如,我们需要TreeSet集合按照学生年龄进行排序:
  1. 让学生类实现Comparable接口:
public class Student implements Comparable<Student>{
}
  1. 重写compareTo方法:
    此方法返回一个int值,若值大于0,则排后面;小于0,排前面;等于0,不储存。
    当然如果年龄相同姓名不同需要根据compareTo方法默认对字符串的比较来排序。
 @Override
    public int compareTo(Student student) {
        int num=this.age-student.age;
        int num2=num==0?this.name.compareTo(student.name):num;
        return num2;
    }
  1. 这时在打印TreeSet的存储数据,就不会在出现系统报错。并且会根据重写的方法,按照年龄进行排序:
    在这里插入图片描述
    当然如果想倒排序,只需要重写compareTo方法时返回负值即可:
@Override
    public int compareTo(Student student) {
        int num=this.age-student.age;
        int num2=num==0?this.name.compareTo(student.name):num;
        return -num2;
    }

在这里插入图片描述

比较器排序

当我们使用空参构造创建TreeSet集合时,会只用自然排序,需要实现Comparable接口。我们也可以采用有参构造TreeSet(Comparator < ? super E > comparator),传入一个Comparetor 比较器:

TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
           @Override
           public int compare(Student o1, Student o2) {
               return 0;
           }
       });

比较用来排序的两个参数,现在按照学生姓名长度进行排序(当长度相等时按默认排序方式,姓名相同按年龄排序):

public class Test {
   public static void main(String[] args) {
       TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
           @Override
           public int compare(Student o1, Student o2) {
               int num = o1.getName().length() - o2.getName().length();
               int num2=num==0?o1.getName().compareTo(o2.getName()):num;
               int num3=num2==0?o1.getAge()-o2.getAge():num2;
               return num3;
           }
       });
       treeSet.add(new Student("易烊千玺",20));
       treeSet.add(new Student("王源",18));
       treeSet.add(new Student("王俊凯",23));
       treeSet.add(new Student("Jay Chou",18));
       treeSet.add(new Student("韩梅梅",23));
       treeSet.add(new Student("王俊凯",15));
       for (Student student : treeSet) {
           System.out.println(student);
       }
   }
}

在这里插入图片描述

总结

当我们想存储无重复元素时,通常使用Set集合。Set集合可以保证元素的唯一性。
HashSet效率最高,LinkedHashSet输入输出有序,TreeSet同时满足元素唯一和排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值