Java——Set

一、介绍

Set集合也是Collection集合的子类型,没有特有方法。Set比Collection定义更严谨,Set集合有如下要求

  1. 元素是不能重复的(不能存储两个对象,其equals方法比较返回true,只能存其中一个)
  2. 元素不能保证插入和取出顺序(无序)
  3. 元素是没有索引的

二、常用子类

常用子类有,HashSet、TreeSet、LinkedHashSet。

  1. HashSet:底层由HashMap,底层结构哈希表结构。去重、无索引、无序。哈希表结构的集合,操作效率会非常高。
  2. LinkedHashSet:底层结构链表加哈希表结构,具有哈希表结构的特点,也具有链表的特点。
  3. TreeSet:底层是由TreeMap,底层数据结构红黑树。去重,让存入的元素具有排序(升序排序)。

三、HashSet

3.1、概述

java.util.HashSet是Set接口的实现类,没有特有方法。底层是哈希表结构,具有去重特点。

例子

Set<Integer> set = new HashSet<>();
set.add(100);
set.add(300);
set.add(100);
set.add(200);
System.out.println(set);   // [100, 200, 300] 

3.2、去重原理分析

HashSet底层结构是哈希表结构,去重的实现借助了 两个方法:equals和hashCode方法。equals方法用来比较两个对象是否相等,hashCode方法用来生成哈希值的。

3.3、去重原理

一个元素存入到HashSet中,会先去比较是否有哈希值相同的元素(称为哈希冲突)。

如果哈希值不冲突,可以断定两个对象是肯定不一样的。

如果哈希值冲突了,进一步使用equals方法进行比较两个元素是否相同,若不相同可以存入。

3.4、去重注意点

HashSet集合,存储自定义类型元素时,要保证元素的唯一性时,需要在自定义类中重写toString()和equals()方法。

四、LinkedHashSet

LinkedHashSet的特点就是保障存取元素的顺序一致。

如果要考虑存取顺序一致的情况下,可以考虑使用LinkedHashSet。如果存取顺序一致不一致无所谓的情况下,就可以考虑使用HashSet。

例子

Set<Integer> set = new HashSet<>();
set.add(300);
set.add(100);
set.add(200);
System.out.println(set);   // [100, 200, 300]

Set<Integer> set2 = new LinkedHashSet<>();
set2.add(300);
set2.add(100);
set2.add(200);
System.out.println(set2);   // [300, 100, 200]

五、TreeSet

5.1、介绍

java.util.TreeSet集合是Set接口的一个实现类,底层依赖于TreeMap,是一种基于红黑树结构的实现,具有以下特点

  1. 元素去重
  2. 元素没有索引
  3. 元素有排序

TreeSet元素的排序规则,使用元素的自然排序规则,或者创建TreeSet时提供的Comparator比较器进行排序。

5.2、构造方法

// 根据其元素的自然排序进行排序
public TreeSet()

// 根据指定的比较器进行排序
public TreeSet(Comparator<E> comparatro)

5.3、TreeSet自然排序

一个类只要实现了接口Comparable就具备自然排序能力。比如如下类

  1. Short
  2. Double
  3. Integer
  4. String

例子

Set<Integer> set = new TreeSet<>();
set.add(300);
set.add(200);
set.add(100);
System.out.println(set);   // [100, 200, 300]

Set<String> set2 = new TreeSet<>();
set2.add("abc");
set2.add("aaa");
set2.add("ccc");
set2.add("aba");
System.out.println(set2);   // [aaa, aba, abc, ccc]

如果是自定义类型,可以在自定义类中实现类Comparable接口,重写compareTo方法更改自然排序的规则,如下

Student.java
public class Student implements Comparable{
    private String name;
    private int age;

   // 满参、空参构造方法、get、set

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        Student stu = (Student) o;
        int result;
        if(this.age == stu.age){
            result = this.name.compareTo(stu.name);
        }else{
            result = this.age - stu.age;
        }
    }
}

// TreeSetTest.java
public class TreeSetTest {
    public static void main(String[] args) {
        Set<Student> set = new TreeSet<>();
        set.add(new Student("张三", 24)); 
        set.add(new Student("王五", 22));
        set.add(new Student("李四", 22));
        set.add(new Student("张三", 24));
        set.add(new Student("王五", 23));

        Iterator<Student> iter = set.iterator();
        while(iter.hasNext()){
            Student stu = iter.next();
            System.out.println(stu);
            // Student{name='李四', age=22}
            // Student{name='王五', age=22}
            // Student{name='王五', age=23}
            // Student{name='张三', age=24}
        }
    }
}

  

5.4、TreeSet自定义排序

TreeSet可以实现自定义比较器排序,需要使用自定义比较器的构造方法,可以用来存储不具备自然排序能力的数据,也可以用来覆盖自然排序规则。

// 根据指定的比较器进行排序
public TreeSet(Comparator<E> comparator)

Comparator<E>是一个接口,我们需要去实现一个抽象方法compare,我们可以直接定义匿名内部类去实现该方法

public int compare(E o1, E o2){
   return o1 - o2;   // 升序
   // 获取 return o2 - o1;   // 降序  
}

例子

// Student.java
public class Student {
    private String name;
    private int age;

    // 空参、满参构造方法、get、set、
    

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

// TreeSetTest.java
public class TreeSetTest {
    public static void main(String[] args) {
        Set<Student> set = new TreeSet<>();
        set.add(new Student("张三", 24));   // 报错
    }
}

由于上述代码中Student类型没有实现Comparable接口,不具备自然排序的能力,所以就报错了,我们可以在Student中实现Comparable接口并重写compareTo方法自定义排序规则,解决此问题,如下

这样就不会有问题了,集合中顺序是以年龄的升序排序的,如果年龄一致的话,再次比较姓名进行排序。

也可以在不动Student类的情况下改进,在实例化TreeSet对象的时候,添加参数匿名Comparator对象,如下

public class TreeSetTest {
    public static void main(String[] args) {
        Set<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if(o1.getAge() == o2.getAge()){
                    return o1.getName().compareTo(o2.getName());
                }else{
                    return o1.getAge() - o2.getAge();
                }
            }
        });
        set.add(new Student("张三", 24));   // 报错
        set.add(new Student("王五", 22));
        set.add(new Student("李四", 22));
        set.add(new Student("张三", 24));
        set.add(new Student("王五", 23));

        Iterator<Student> iter = set.iterator();
        while(iter.hasNext()){
            Student stu = iter.next();
            System.out.println(stu);
            // Student{name='李四', age=22}
            // Student{name='王五', age=22}
            // Student{name='王五', age=23}
            // Student{name='张三', age=24}
        }
    }
}

六、注意点

当存储的元素具备自然排序,又在TreeSet集合中指定了比较器排序,优先使用比较器排序。

posted @ 2021-05-17 15:27  徐林俊  阅读(525)  评论(0编辑  收藏  举报