Set系列集合

本文概述了Set集合的无序性、HashSet基于哈希表的去重机制,LinkedHashSet的有序特性,以及TreeSet的自定义排序方法。重点讲解了如何通过重写equals和hashCode方法实现对象去重,并演示了如何利用Comparator定制排序规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Set系列集合概述

无序只是在存数据的时候元素会变得无序,当我们去访问的时候,他的顺序是不会再变的

 

 

Set集合是继承Collection集合的,所以Set集合的API和collection集合的差不多相同

set没有额外拓展功能,几乎只有Collection集合的功能

 

2.HashSet元素无序的底层原理

HashSet是基于哈希表实现的

哈希表的组成:Jdk1.8之前:数组+链表

jdk1.8之后:数组+链表+红黑树

 

哈希值是通过对象地址得到的一个整数,可以理解为一个地址,但他不是一个地址

每个对象都有哈希值

 

hashSet无序的原因

找元素就是:通过要找元素的哈希值跟数组的长度取余得出改元素在数组中的位置,然后跟该位置上的元素一个一个比较,扎到元素就输出

 

 

 

通过哈希值进行大小排列,哈希值小的往左边走,哈希值小的往右边走

 

 

ArrayList是基于数组实现的,创建的数组长度为10。当加入第十一个元素的时候就新建数组,扩容为原来的1.5倍

HashSet集合是基于哈希表实现的,创建的数组长度为16,当存满数组长度的0.75时,就开始扩容为原来的两倍,扩容都是新建数组,在复制元素到新数组

3.HashSet去重复的原理

 

默认机制:先判断哈希值是否相同,再去判断值是否相同

只有哈希值和内容一样,才去判断这两个内容是否一样

哈希值一样就会被放到同一个位置的链表中去

HashSet不能去重对象

 

此时s1和s2的对象地址不一样,哈希值就不一样,所以默认机制判断这两个对象不一样

但是我们new对象的时候地址就可能不一样,但是内容一样,我们希望内容一样就判定为是同一对象

这时,我们就要去重写hashcode和equal方法

 

当自定义对象使用HashSet的时候,我们就应该想到重写hashcode和equal方法

package com.Set;
/**
 * 了解hashSet去重的机制
 * 重写hascode和equal方法实现对象内容相同就判定这里两个对象相同,符合实际开发
 */
​
import java.util.HashSet;
​
public class SetDemo3 {
    public static void main(String[] args) {
        //HashSet的去重的默认机制先判断两个对象的哈希值是否一样,不一样才判断对象里面的内容是否一样
        Student student1=new Student("lingyi",18,'男');
        Student student2=new Student("lingyi",18,'男');
        Student student3=new Student("ting",20,'女');
        System.out.println(student1.equals(student2));
​
        HashSet<Student>  sets=new HashSet<>();
        sets.add(student1);
        sets.add(student2);
        sets.add(student3);
        System.out.println(sets);
        //[Student{name='lingyi', age=18, sex=男}, Student{name='ting', age=20, sex=女}, Student{name='lingyi', age=18, sex=男}]
​
        //在Student类中重写hashcode和equal方法,解决
        //[Student{name='ting', age=20, sex=女}, Student{name='lingyi', age=18, sex=男}]
​
​
    }
}
​

Student类

package com.Set;
​
import java.util.Objects;
​
public class Student {
    private String name;
    private int age;
    private  char sex;
​
    public Student() {
    }
​
    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
​
    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 char getSex() {
        return sex;
    }
​
    public void setSex(char sex) {
        this.sex = sex;
    }
​
    /**
     * equal方法默认比较的是对象的地址
     * 重写equal方法:比较对象的内容,而不是比较地址,只要内容一样,就判定这两个对象一样
     * @param o
     * @return
     */
    @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 &&
                sex == student.sex &&
                Objects.equals(name, student.name);
    }
​
    /**
     * hascode方法默认传入的是对象的地址,我们把对象内容作为参数
     * 重写hascode方法,只要两个对象的内容一样,就返回相同的哈希值
     * 重写来了hashcode方法,我们王HashSet里面添加元素的时候就不会重复了
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
}
​

/**
     * equal方法默认比较的是对象的地址
     * 重写equal方法:比较对象的内容,而不是比较地址,只要内容一样,就判定这两个对象一样
     * @param o
     * @return
     */
    @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 &&
                sex == student.sex &&
                Objects.equals(name, student.name);
    }
​
    /**
     * hascode方法默认传入的是对象的地址,我们把对象内容作为参数
     * 重写hascode方法,只要两个对象的内容一样,就返回相同的哈希值
     * 重写来了hashcode方法,我们王HashSet里面添加元素的时候就不会重复了
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }

 

4.LinkedHashSet集合概述和特点

 

有序是通过双链表实现的,在每个元素上面都加上上一个元素和下一个元素的地址

读取的时候顺着双链表输出

牺牲了一些内存来记录顺序

 

5.treeSet

 

 

字符串默认按照首字母的ascll编码升序排序.数字 < 大写字母 < 小写字母 < 文字

Double和Integer都是按照数字升序

对于自定义类型,TreeSet是不知道如何进行排序的,我们要自己制定排序规则

两种方式:

①类去实现Comparetor接口,重写里面的compareTo方法

②使用TreeSet集合的Comparator比较器(在定义Tree的时候使用)

 

 

 

要是TreeSet集合和实体类中都有比较器,那么会默认调用TreeSet方法里面的比较器(就近原则)

 

 

注意:当比较的是double类型的时候,不能直接比较,要使用Doube的比较器,因为返回类型是int,12.5-12.0等于0,这就出问题了

这会删掉一个元素

 

 

package com.Set;
​
import java.util.*;
​
/**
 * 1.观察TreeSet对于有值特性的数据如何排序
 * 2.学会对自定义类型对象进行制定规则排序
 */
​
public class treeSetDemo {
    public static void main(String[] args) {
        //使用多态和泛型创建TreeSet对象
​
        //1.Integer类型
        Set<Integer> tree=new TreeSet<>();// 特点:不重复,无索引、可排序
        tree.add(12);
        tree.add(4);
        tree.add(56);
        tree.add(23);
        System.out.println(tree);
        //[4, 12, 23, 56]默认升序
​
​
        //1.Double类型
        Set<Double> tree2=new TreeSet<>();// 特点:不重复,无索引、可排序
        tree2.add(12.0);
        tree2.add(4.0);
        tree2.add(56.0);
        tree2.add(23.0);
        System.out.println(tree2);
        //[4.0, 12.0, 23.0, 56.0]默认升序
​
​
​
        //1.String类型
        Set<String> tree3=new TreeSet<>();// 特点:不重复,无索引、可排序
        tree3.add("java");
        tree3.add("about");
        tree3.add("Java");
        tree3.add("About");
        tree3.add("黑马");
        tree3.add("23");
        System.out.println(tree3);
        //[23, About, Java, about, java, 黑马]
        //
​
​
​
        //自定义对象
        //对于自定义类型,TreeSet是不知道如何进行排序的,我们要自己制定排序规则
​
        //第一种:让类继承Comparator接口使用泛型,与本类对象进行比较
//        TreeSet<Student> treeSet=new TreeSet<>();
//        Student student1=new Student("lingyi",18,'男');
//        Student student2=new Student("lingyi",18,'男');
//        Student student3=new Student("ting",20,'女');
//        Collections.addAll(treeSet,student1,student2,student3);
//        System.out.println(treeSet);
​
        //第二种:使用TreeSet的比较器
        TreeSet<Student>treeSet=new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按照年龄升序
                return  o1.getAge()-o2.getAge();
            }
        });
        Student student1=new Student("lingyi",18,'男');
        Student student2=new Student("lingyi",18,'男');
        Student student3=new Student("ting",20,'女');
        Collections.addAll(treeSet,student1,student2,student3);
        System.out.println(treeSet);
​
    }
}

Student类

package com.Set;
​
import java.util.Comparator;
import java.util.Objects;
​
public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private  char sex;
​
    public Student() {
    }
​
    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
​
    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 char getSex() {
        return sex;
    }
​
    public void setSex(char sex) {
        this.sex = sex;
    }
​
​
    /**
     * equal方法默认比较的是对象的地址
     * 重写equal方法:比较对象的内容,而不是比较地址,只要内容一样,就判定这两个对象一样
     * @param o
     * @return
     */
    @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 &&
                sex == student.sex &&
                Objects.equals(name, student.name);
    }
​
    /**
     * hascode方法默认传入的是对象的地址,我们把对象内容作为参数
     * 重写hascode方法,只要两个对象的内容一样,就返回相同的哈希值
     * 重写来了hashcode方法,我们王HashSet里面添加元素的时候就不会重复了
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
​
    @Override
    public int compareTo(Student o) {
        //this表示当前对象
        return this.getAge()-o.getAge();
    }
}
​

 

我们优先使用TreeSet集合的比较器,因为更简单,方便。默认也是使用的集合的比较器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值