首页 > 编程笔记 > Java笔记 阅读:8

Java Set集合类的用法(非常详细)

Set 是 Collection 接口的子接口,Set 接口没有提供额外的方法。

Set 类型的集合不允许包含相同的元素,若把两个相同的元素加入同一个 Set 类型的集合中,则添加操作会失败。

Set 类型的集合支持的遍历方式和 Collection 类型的集合一样,可以用 foreach 和 iterator。Set 接口的常用实现类有 HashSet、TreeSet。

Java HashSet实现类

HashSet 是 Set 接口的主要实现类,使用 Set 类型的集合时通常会使用这个实现类。

HashSet 用哈希算法存储集合中的元素,因此具有很好的存储、查找、删除等性能。HashSet 不能保证元素的排列顺序,不是线程安全的,集合元素可以是 null。

【实例】创建集合,使用 HashSet 实现集合中基本的功能,代码如下:
import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {
        SetDemo setDemo = new SetDemo();
        setDemo.testSet();
    }

    public void testSet() {
        // 创建 HashSet 集合并添加元素
        HashSet hashSet = new HashSet();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("c");
        System.out.println(hashSet);
    }
}
在上述代码中,调用 add() 方法实现了元素添加,使用的是 Collection 接口的方法。在 Set 接口中没有可以修改的方法,可以查看 Collection 接口的方法,直接使用即可。

判断集合中两个元素相等的标准:两个对象通过 hashCode() 方法得到的哈希值相等,且两个对象的 equals() 方法返回值为 true。

对存放在 Set 类型的集合中的对象,对应的类一定要重写 hashCode() 和 equals() 方法,以符合对象相等规则,即“相等的对象必须具有相等的哈希值”。

HashSet 类型的集合中元素的无序性不等同于随机性,这里的无序性与元素的添加位置有关。具体来说,我们在添加每个元素到数组中时,具体的存储位置都是由元素的 hashCode() 调用后返回的哈希值决定的,导致在数组中的每个元素不是依次紧密存放的,表现出一定的无序性。

【实例】创建集合和 Student 类,在 HashSet 类型的集合中判断两个对象是否相等,代码如下:
import java.util.Objects;

public class Student {
    private Integer id;
    private String name;
    private Integer age;

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

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) &&
               Objects.equals(name, student.name) &&
               Objects.equals(age, student.age);
    }

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

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

import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {
        Student student = new Student(1, "张三", 20);
        Student student02 = new Student(1, "张三", 20);

        HashSet hashSet = new HashSet();
        hashSet.add(student);
        hashSet.add(student02);

        System.out.println(hashSet);
    }
}
程序运行结果为:

[Student{id=1, name='张三', age=20}]

上述代码创建了 Student 类,并在 Student 类中实现了 hashCode() 方法和 equals() 方法。两个对象通过调用 HashSet 类型集合的 hashCode() 和 equals() 方法来确定元素的唯一性,如果两个对象的 hashCode() 返回值相同,并且 equals() 方法返回 true,HashSet 类型集合就会认为这两个对象是相同的元素,因此只保留其中一个,无法重复添加。

Java TreeSet实现类

TreeSet 是 SortedSet 接口的实现类,可根据指定的属性顺序对集合中的元素。进行遍历,TreeSet 底层使用红黑树结构存储数据。

TreeSet 有两种排序方法,分别是自然排序和定制排序。在默认情况下,TreeSet 采用自然排序。

1) 自然排序

TreeSet 首先调用 compareTo() 方法来比较元素的大小,然后将集合元素按升序(默认情况)排列。

【实例】创建集合和 Student 类,按照对象的年龄从小到大进行排序,采用自然排序方法,代码如下:
import java.util.Comparator;
import java.util.Objects;

public class Student implements Comparable<Student> {

    private Integer id;
    private String name;
    private Integer age;

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

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) &&
               Objects.equals(name, student.name) &&
               Objects.equals(age, student.age);
    }

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

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

    @Override
    public int compareTo(Object o) {
        if (this == o) {
            return 0;
        }
        if (o instanceof Student) {
            Student student = (Student) o;
            int value = this.age - student.age;
            if (value != 0) {
                return value;
            }
            // 年龄相同时按姓名降序
            return -this.name.compareTo(student.name);
        }
        throw new RuntimeException("输入的类型不匹配");
    }
}

import java.util.TreeSet;

public class SetDemo {
    public static void main(String[] args) {
        Student student   = new Student(1, "王五", 20);
        Student student02 = new Student(2, "张三", 30);
        Student student03 = new Student(3, "小花", 33);
        Student student04 = new Student(4, "笑笑", 24);

        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(student);
        treeSet.add(student02);
        treeSet.add(student03);
        treeSet.add(student04);

        System.out.println(treeSet);
    }
}
程序运行结果为:

[Student{id=1, name='王五', age=20}, Student{id=4, name='笑笑', age=24}, Student{id=2, name='张三', age=30}, Student{id=3, name='小花', age=33}]

在上述代码中,把一个对象添加到 treeSet 集合中时,该对象的类必须实现 Comparable 接口,实现 Comparable 接口的类必须实现 compareTo() 方法,两个对象通过 compareTo() 方法的返回值来比较大小。

2) 定制排序

如果对象所属的类没有实现 Comparable 接口,或不希望按照升序(默认情况)排列,就可以考虑使用定制排序。定制排序通过 Comparator 接口实现,需要重写 compare(Object o1, Object o2) 方法。

【实例】创建集合和 Student 类,按照对象中的年龄从小到大进行排序,采用定制排序方法,代码如下:
import java.util.Objects;

public class Student02 {
    private Integer id;
    private String name;
    private Integer age;

    public Student02(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Student02() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student02 student = (Student02) o;
        return Objects.equals(id, student.id) &&
               Objects.equals(name, student.name) &&
               Objects.equals(age, student.age);
    }

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

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

import java.util.Comparator;
import java.util.TreeSet;

public class SetDemo04 {
    public static void main(String[] args) {
        Student02 student   = new Student02(1, "王五", 20);
        Student02 student02 = new Student02(2, "张三", 30);
        Student02 student04 = new Student02(4, "笑笑", 24);
        Student02 student03 = new Student02(3, "小花", 33);

        Comparator<Student02> comparator = new Comparator<Student02>() {
            @Override
            public int compare(Student02 o1, Student02 o2) {
                if (o1 instanceof Student02 && o2 instanceof Student02) {
                    Student02 s1 = (Student02) o1;
                    Student02 s2 = (Student02) o2;
                    int value = s1.getAge() - s2.getAge();
                    if (value != 0) {
                        return value;
                    }
                    return s1.getName().compareTo(s2.getName());
                }
                throw new RuntimeException("输入的类型不匹配");
            }
        };

        TreeSet<Student02> treeSet = new TreeSet<>(comparator);
        treeSet.add(student);
        treeSet.add(student02);
        treeSet.add(student03);
        treeSet.add(student04);

        System.out.println(treeSet);
    }
}
程序运行结果为:

[Student{id=1, name='王五', age=20}, Student{id=4, name='笑笑', age=24}, Student{id=2, name='张三', age=30}, Student{id=3, name='小花', age=33}]

在上述代码中,利用 compare(Object o1, Object o2) 方法比较 o1 和 o2 的大小:若返回正整数,则表示 o1 大于 o2;若返回 0,则表示两者相等;若返回负整数,则表示 o1 小于 o2。要实现定制排序,需要将实现 Comparator 接口的实例作为形式参数传递给 TreeSet 的构造器。

相关文章