Java类集框架-Set

Set集合

Set集合概述和特点

代码实现

Set集合的实现——TreeSet

特点

(1)自然排序-Comparable

 (2)按照指定的比较器排序-Comparator        

Set集合的实现——HashSet

特点

基本使用

哈希值

哈希表结构

HashSet集合存储学生对象并遍历


Set集合

Set集合概述和特点

  • 不可以存储重复元素

  • 存取的顺序不一致

  • 没有索引,不能使用普通for循环遍历

代码实现

案例:存储字符串并遍历

public class Demo{
    public static void main(String[] args) {
      	//创建集合对象
        Set<String> set = new TreeSet<>();
      	//添加元素
        set.add("ccc");
        set.add("aaa");
        set.add("aaa");
        set.add("bbb");
      
      	//(1)遍历集合
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------------------------");
        
        //(2)遍历集合
        for (String s : set) {
            System.out.println(s);
        }
    }
}

遇到重复元素, set集合会自动帮我们去重!

Set集合的实现——TreeSet

特点

  • 不可以存储重复元素

  • 没有索引

  • 可以将元素按照规则进行排序

    • TreeSet():根据其元素的自然排序进行排序

    • TreeSet(Comparator comparator) :根据指定的比较器进行排序

(1)自然排序-Comparable

按照字典序,进行排序。

public class Demo03 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>();
        ts.add("aaa");
        ts.add("bb");
        ts.add("cc");
        ts.add("dd");
        ts.add("a");
        System.out.println(ts);
        //[a, aaa, bb, cc, dd]
    }
}

案例(一):

存储学生对象并遍历,创建TreeSet集合使用无参构造方法

要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

实现步骤:

  1. 使用空参构造创建TreeSet集合。用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  2. 自定义的Student类实现Comparable接口。自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  3. 重写接口中的compareTo方法。重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

代码实现:

public class Demo03 {
    public static void main(String[] args) {
        TreeSet<Student> stu = new TreeSet<>();

        Student s1 = new Student("小花花",27);
        Student s2 = new Student("小花",29);
        Student s3 = new Student("小明",25);
        Student s4 = new Student("小黑",25);

        stu.add(s1);
        stu.add(s2);
        stu.add(s3);
        stu.add(s4);

        System.out.println(stu);
        //[Student{name='小明', age=25}, Student{name='小花花', age=27}, Student{name='小花', age=29}]
    }
}
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student(){
    }

    public Student(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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    @Override
    public int compareTo(Student o) {
        int result = this.age - o.age;
        return result;
    }
}

上述代码中,需要深究的是我们重写的comparaTo方法

@Override
public int compareTo(Student o) {
        int result = this.age - o.age;
        return result;
}

案例(二):

在上述案例(一)的基础上,进行改进

存储学生对象并遍历,创建TreeSet集合使用无参构造方法

要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

代码实现

上述其他的代码不变,只要修改comparaTo()中的内容

@Override
public int compareTo(Student o) {
    int result = this.age - o.age;
    result = result == 0 ? this.name.compareTo(o.getName()) : result;
    return result;
}

关于comparaTo()方法在菜鸟编程上有这样子的解释

 (2)按照指定的比较器排序-Comparator        

  • TreeSet的带参构造方法使用的就是比较器排序对元素进行排序
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compara(T o1,To2)方法。
  • 重写方法时,一定要注意排序规则必须按照要求主要条件和次要条件来写。

案例分析:

依然使用上述案例的环境,使用Comparator的方式实现

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

    public Student(){
    }

    public Student(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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Demo03 {
    public static void main(String[] args) {
        TreeSet<Student> stu = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //o1表示现在要存入的那个元素
                //o2表示已经存入到集合中的元素
                //主要条件
                int result = o1.getAge() - o2.getAge();
                //次要条件
                result = result == 0 ?  o1.getName().compareTo(o2.getName()) :result;
                return result;
            }
        });

        Student s1 = new Student("小花花",27);
        Student s2 = new Student("小花",29);
        Student s3 = new Student("小明",25);
        Student s4 = new Student("小黑",25);

        stu.add(s1);
        stu.add(s2);
        stu.add(s3);
        stu.add(s4);

        System.out.println(stu);
    }
}

两种比较方式小结

自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序

比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序

两种方式中关于返回值的规则

如果返回值为负数,表示当前存入的元素是较小值,存左边

如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存

如果返回值为正数,表示当前存入的元素是较大值,存右边

案例-比较字符串

按照长度排序,如果长度一样则按照首字母排序!

实现方式一:

public class Demo {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int result = o1.length() - o2.length();
                result = result == 0 ? o1.compareTo(o2) : result;
                return result;
            }
        });

        ts.add("d");
        ts.add("asd");
        ts.add("gre");
        ts.add("zfsdfsf");

        System.out.println(ts);
        //[d, asd, gre, zfsdfsf]
    }
}

实现方式二:

public class Demo {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>(
                (String o1,String o2) -> {
                    int result = o1.length() - o2.length();
                    result = result == 0 ? o1.compareTo(o2) : result;
                    return result;
                }
        );
        ts.add("d");
        ts.add("asd");
        ts.add("gre");
        ts.add("zfsdfsf");

        System.out.println(ts);
        //[d, asd, gre, zfsdfsf]
    }
}

Set集合的实现——HashSet

特点

  • 底层数据结构是哈希表

  • 存取无序

  • 不可以存储重复元素

  • 没有索引,不能使用普通for循环遍历

基本使用

public class Demo {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> set = new HashSet<String>();

        //添加元素
        set.add("hello");
        set.add("world");
        set.add("java");
        //不包含重复元素的集合
        set.add("world");

        //遍历
        for (String s : set) {
            System.out.println(s);
            //world
            //java
            //hello
        }

    }
}

哈希值

哈希值简介

是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

如何获取哈希值

Object类中的public int hashCode():根据对象的地址值,返回计算出的哈希值

Coding

public class Student {
    private String name;
    private int 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 Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class HashCodeDemo {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan",19);
        Student s2 = new Student("xiaoming",78);

        System.out.println(s1.hashCode());
        //356573597
        System.out.println(s2.hashCode());
        //1735600054
        System.out.println(s1.hashCode());
        //356573597
    }
}

上述代码演示,使用实例化Student对象s1,s2,它们调用hashCode()方法,根据创建的对象的地址返回它们对应的hash值。

我们可以查看IDEA中的hashCode的模板

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

当然我们可以对上述模板行进重写,就一般是根据对象的属性值来计算哈希值。此时,跟对象的地址值就没有关系

哈希值的特点

同一个对象多次调用hashCode()方法返回的哈希值是相同的

默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

哈希表结构

JDK1.8以前

JDK1.8之后

关于红黑树的详细介绍如下所示:

二叉树之红黑树icon-default.png?t=M0H8https://blog.csdn.net/weixin_43715214/article/details/122811540

HashSet集合存储学生对象并遍历

案例需求

创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合

要求:学生对象的成员变量值相同,我们就认为是同一个对象

代码实现

学生类

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

    public Student() {
    }

    public Student(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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

测试类

public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        Student s4 = new Student("王祖贤", 33);

        //把学生添加到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //遍历集合(增强for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法

posted @   金鳞踏雨  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示